mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-27 09:33:08 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77973f0037 | ||
|
|
7f8f39916d | ||
|
|
cc8bf6744b | ||
|
|
bf17d6894f | ||
|
|
900f5b136f | ||
|
|
194a9c0c40 | ||
|
|
f6f9b768cc | ||
|
|
8fe0148821 | ||
|
|
28ed2fe8f2 | ||
|
|
131df2d82a | ||
|
|
0f5ec6c40c | ||
|
|
39cbadb100 | ||
|
|
0afb503860 | ||
|
|
ed1c589e2d | ||
|
|
864cc7a7bb | ||
|
|
657a374895 | ||
|
|
35e1ce0df2 | ||
|
|
2065ff80ff | ||
|
|
b24bba87d9 | ||
|
|
5583fc76f3 | ||
|
|
e810445ac9 | ||
|
|
5afbb4ee4e | ||
|
|
6ae4cd143c | ||
|
|
978329fdc9 | ||
|
|
f754ecd6c3 | ||
|
|
62a827ce49 | ||
|
|
e971da2b59 | ||
|
|
2d092cb290 | ||
|
|
5b66ecb06f | ||
|
|
22a9799674 | ||
|
|
7c813be13a | ||
|
|
e94148b2f0 | ||
|
|
ec4f6e4327 | ||
|
|
fcbd117784 | ||
|
|
dab716a9e0 | ||
|
|
9265e25c73 | ||
|
|
4ad63d5bce | ||
|
|
f89c897488 | ||
|
|
521ff5e7e3 | ||
|
|
89d8cb3b0a | ||
|
|
a0e92b54d0 | ||
|
|
62f549f038 | ||
|
|
e8f3b0c8d0 | ||
|
|
587ce78f4e | ||
|
|
7ca1dd3c68 | ||
|
|
58543f0b4d | ||
|
|
e88253f364 | ||
|
|
bdf37d8fe7 | ||
|
|
22908207a3 | ||
|
|
7239b89108 | ||
|
|
0ea80bd758 | ||
|
|
f6d623ace3 | ||
|
|
63ad8b3411 | ||
|
|
50cc757a5c | ||
|
|
70430f84e1 | ||
|
|
80db261c88 | ||
|
|
5631ef7be7 | ||
|
|
2745a4d6c1 | ||
|
|
33190b5c89 | ||
|
|
354a5832e4 | ||
|
|
f57c0f0886 | ||
|
|
954a393fce | ||
|
|
01dbac78ce | ||
|
|
594ea8ab59 | ||
|
|
033493a31b | ||
|
|
e79b099633 | ||
|
|
5bc948ab0a | ||
|
|
28c5d7d84a | ||
|
|
b9cfc2e6af |
@@ -6,7 +6,7 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:8.9
|
- image: circleci/node:12.16.3
|
||||||
- image: circleci/mongo:3.4-jessie
|
- image: circleci/mongo:3.4-jessie
|
||||||
|
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
@@ -30,4 +30,3 @@ jobs:
|
|||||||
|
|
||||||
# run tests!
|
# run tests!
|
||||||
- run: npm run circleci
|
- run: npm run circleci
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
env : {
|
env : {
|
||||||
browser : true,
|
browser : true,
|
||||||
|
node : true
|
||||||
},
|
},
|
||||||
plugins : ['react'],
|
plugins : ['react'],
|
||||||
rules : {
|
rules : {
|
||||||
@@ -54,7 +55,7 @@ module.exports = {
|
|||||||
'array-bracket-spacing' : ['warn', 'never'],
|
'array-bracket-spacing' : ['warn', 'never'],
|
||||||
'arrow-spacing' : ['warn', { before: false, after: false }],
|
'arrow-spacing' : ['warn', { before: false, after: false }],
|
||||||
'comma-spacing' : ['warn', { before: false, after: true }],
|
'comma-spacing' : ['warn', { before: false, after: true }],
|
||||||
'indent' : ['warn', 'tab'],
|
'indent' : ['warn', 'tab', { 'MemberExpression': 'off' }],
|
||||||
'keyword-spacing' : ['warn', {
|
'keyword-spacing' : ['warn', {
|
||||||
before : true,
|
before : true,
|
||||||
after : true,
|
after : true,
|
||||||
@@ -66,7 +67,7 @@ module.exports = {
|
|||||||
multiLine : { beforeColon: true, afterColon: true, align: 'colon' },
|
multiLine : { beforeColon: true, afterColon: true, align: 'colon' },
|
||||||
singleLine : { beforeColon: false, afterColon: true }
|
singleLine : { beforeColon: false, afterColon: true }
|
||||||
}],
|
}],
|
||||||
'linebreak-style' : ['warn', 'unix'],
|
'linebreak-style' : 'off',
|
||||||
'no-trailing-spaces' : 'warn',
|
'no-trailing-spaces' : 'warn',
|
||||||
'no-whitespace-before-property' : 'warn',
|
'no-whitespace-before-property' : 'warn',
|
||||||
'object-curly-spacing' : ['warn', 'always'],
|
'object-curly-spacing' : ['warn', 'always'],
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ config/local.*
|
|||||||
|
|
||||||
todo.md
|
todo.md
|
||||||
startDB.bat
|
startDB.bat
|
||||||
|
startMViewer.bat
|
||||||
|
|||||||
15
Dockerfile
15
Dockerfile
@@ -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
12
README.DOCKER.md
Normal 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`
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
47
changelog.md
47
changelog.md
@@ -1,5 +1,22 @@
|
|||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
### Monday, 12/10/2020 - v2.10.1
|
||||||
|
- Fixed issue with users unable to create new brews
|
||||||
|
- Fixing brews being lost when loaded via back button
|
||||||
|
|
||||||
|
### Wednesday, 07/10/2020 - v2.10.0
|
||||||
|
- Google Drive integration -- Sign in with your Google account to link it with your Homebrewery profile. A new button in the Edit page will let you transfer your file to your personal Google Drive storage, and Google will keep a backup of each version! No more lost work surprises!
|
||||||
|
|
||||||
|
### Friday, 28/08/2020 - v2.9.2
|
||||||
|
- Many dependency updates
|
||||||
|
- Finally fixed this changelog page to not run off the edge :P
|
||||||
|
|
||||||
|
### Sunday, 19/07/2020 - v2.9.1
|
||||||
|
- Fixed paragraphs appearing blank on new columns
|
||||||
|
|
||||||
|
### Wednesday, 20/05/2020 - v2.9.0
|
||||||
|
- Major refactoring of site backend to work with updated dependencies for security (should be invisible to users)
|
||||||
|
|
||||||
### Wednesday, 11/03/2020 - v2.8.2
|
### Wednesday, 11/03/2020 - v2.8.2
|
||||||
- Fixed delete button removing everyone's copy for brews with multiple authors
|
- Fixed delete button removing everyone's copy for brews with multiple authors
|
||||||
- Compressed homebrew text in database
|
- Compressed homebrew text in database
|
||||||
@@ -27,27 +44,27 @@
|
|||||||
### Saturday, 22/04/2017 - v2.7.4
|
### Saturday, 22/04/2017 - v2.7.4
|
||||||
- Give ability to hide the render warning notification
|
- Give ability to hide the render warning notification
|
||||||
|
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
### Friday, 03/03/2017 - v2.7.3
|
### Friday, 03/03/2017 - v2.7.3
|
||||||
- Increasing the range on the Partial Page Rendering for a quick-fix for it getting out of sync on long brews.
|
- Increasing the range on the Partial Page Rendering for a quick-fix for it getting out of sync on long brews.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Saturday, 18/02/2017 - v2.7.2
|
### Saturday, 18/02/2017 - v2.7.2
|
||||||
- Adding ability to delete a brew from the user page, incase the user creates a brew that makes the edit page unrender-able. (re:309)
|
- Adding ability to delete a brew from the user page, incase the user creates a brew that makes the edit page unrender-able. (re:309)
|
||||||
|
|
||||||
## BIG NEWS
|
|
||||||
With the next major release of Homebrewery, v3.0.0, this tool *will no longer support raw HTML input for brew code*. Most issues and errors users are having are because of this feature and it's become too taxing to help and fix these issues.
|
|
||||||
|
|
||||||
All brews made previous to the release of v3.0.0 will still render normally.
|
|
||||||
|
|
||||||
### Thursday, 19/01/2017 - v2.7.0
|
### Thursday, 19/01/2017 - v2.7.0
|
||||||
- Fixed saving multiple authors and multiple systems on brew metadata (thanks u/PalaNolho re:282)
|
- Fixed saving multiple authors and multiple systems on brew metadata (thanks u/PalaNolho re:282)
|
||||||
- Adding in line highlight for new pages
|
- Adding in line highlight for new pages
|
||||||
- Added in a simple brew lookup for admin
|
- Added in a simple brew lookup for admin
|
||||||
|
|
||||||
|
|
||||||
### Saturday, 14/01/2017 - v2.7.0
|
### Saturday, 14/01/2017 - v2.7.0
|
||||||
- Added a new Render Warning overlay. It detects situations where the brew may not be rendering correctly (wrong browser, browser is zoomed in...) and let's the user know
|
- Added a new Render Warning overlay. It detects situations where the brew may not be rendering correctly (wrong browser, browser is zoomed in...) and let's the user know
|
||||||
|
|
||||||
|
|
||||||
### Sunday, 25/12/2016 - v2.7.0
|
### Sunday, 25/12/2016 - v2.7.0
|
||||||
- Switching over to using Vitreum v4
|
- Switching over to using Vitreum v4
|
||||||
- Removed gulp, all tasks are run through npm scripts
|
- Removed gulp, all tasks are run through npm scripts
|
||||||
@@ -60,8 +77,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
|
|||||||
- Removed a lot of unused files in shared
|
- Removed a lot of unused files in shared
|
||||||
- vitreum v4 now lets me use codemirror as a pure node dependacy
|
- vitreum v4 now lets me use codemirror as a pure node dependacy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Saturday, 03/12/2016 - v2.6.0
|
### Saturday, 03/12/2016 - v2.6.0
|
||||||
- Added report back to the edit page
|
- Added report back to the edit page
|
||||||
- Changed metaeditor icon
|
- Changed metaeditor icon
|
||||||
@@ -73,13 +88,11 @@ All brews made previous to the release of v3.0.0 will still render normally.
|
|||||||
- Added a table of contents snippet (thanks u/tullisar)
|
- Added a table of contents snippet (thanks u/tullisar)
|
||||||
- Added a multicolumn snippet
|
- Added a multicolumn snippet
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
### Thursday, 01/12/2016
|
### Thursday, 01/12/2016
|
||||||
- Added in a snippet for a split table
|
- Added in a snippet for a split table
|
||||||
- Added an account nav item to new page
|
- Added an account nav item to new page
|
||||||
|
|
||||||
|
|
||||||
### Sunday, 27/11/2016 - v2.5.1
|
### Sunday, 27/11/2016 - v2.5.1
|
||||||
- Fixed the column rendering on the new user page. Really should have tested that better
|
- Fixed the column rendering on the new user page. Really should have tested that better
|
||||||
- Added a hover tooltip to fully read the brew description
|
- Added a hover tooltip to fully read the brew description
|
||||||
@@ -95,7 +108,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
|
|||||||
- Added a new user page to see others published brews, as well as all of your own brews.
|
- Added a new user page to see others published brews, as well as all of your own brews.
|
||||||
- Added a new nav item for accessing your profile and logging in
|
- Added a new nav item for accessing your profile and logging in
|
||||||
|
|
||||||
|
|
||||||
### Monday, 14/11/2016
|
### Monday, 14/11/2016
|
||||||
- Updated snippet bar style
|
- Updated snippet bar style
|
||||||
- You can now print from a new page without saving
|
- You can now print from a new page without saving
|
||||||
@@ -120,6 +132,8 @@ All brews made previous to the release of v3.0.0 will still render normally.
|
|||||||
- Fixed the noteblock overlapping into titles (thanks u/dsompura!)
|
- Fixed the noteblock overlapping into titles (thanks u/dsompura!)
|
||||||
- Fixed a bad search route in the admin panel (thanks u/SnappyTom!)
|
- Fixed a bad search route in the admin panel (thanks u/SnappyTom!)
|
||||||
|
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
### Friday, 29/07/2016 - v2.2.7
|
### Friday, 29/07/2016 - v2.2.7
|
||||||
- Adding in descriptive note blocks. (Thanks calculuschild!)
|
- Adding in descriptive note blocks. (Thanks calculuschild!)
|
||||||
@@ -141,16 +155,16 @@ All brews made previous to the release of v3.0.0 will still render normally.
|
|||||||
- Added in a new auto-incremeting page number snippet (thakns u/Ryrok!)
|
- Added in a new auto-incremeting page number snippet (thakns u/Ryrok!)
|
||||||
- Lists in monster stat blocks should be fixed now
|
- Lists in monster stat blocks should be fixed now
|
||||||
|
|
||||||
|
|
||||||
### Saturday, 04/06/2016 - v2.2.0
|
### Saturday, 04/06/2016 - v2.2.0
|
||||||
- MIgrating The Homebrewery over to hombrewery.naturalcrit.com. It know runs on it's own server, with it's own repo separate from the other tools I'm working on. Makes updating and deploying much easier.
|
- MIgrating The Homebrewery over to hombrewery.naturalcrit.com. It know runs on it's own server, with it's own repo separate from the other tools I'm working on. Makes updating and deploying much easier.
|
||||||
|
|
||||||
\page
|
|
||||||
|
|
||||||
### Sunday, 29/05/2016 - v2.1.0
|
### Sunday, 29/05/2016 - v2.1.0
|
||||||
- Finally added a syntax for doing spell lists. A bit in-depth about why this took so long. Essentially I'm running out of syntax to use in stardard Markdown. There are too many unique elements in the PHB-style to be mapped. I solved this earlier by stacking certain elements together (eg. an `<hr>` before a `blockquote` turns it into moster state block), but those are getting unweildly. I would like to simply wrap these in `div`s with classes, but unfortunately Markdown stops processing when within HTML blocks. To get around this I wrote my own override to the Markdown parser and lexer to process Markdown within a simple div class wrapper. This should open the door for more unique syntaxes in the future. Big step!
|
- Finally added a syntax for doing spell lists. A bit in-depth about why this took so long. Essentially I'm running out of syntax to use in stardard Markdown. There are too many unique elements in the PHB-style to be mapped. I solved this earlier by stacking certain elements together (eg. an `<hr>` before a `blockquote` turns it into moster state block), but those are getting unweildly. I would like to simply wrap these in `div`s with classes, but unfortunately Markdown stops processing when within HTML blocks. To get around this I wrote my own override to the Markdown parser and lexer to process Markdown within a simple div class wrapper. This should open the door for more unique syntaxes in the future. Big step!
|
||||||
- Override Ctrl+P (and cmd+P) to launch to the print page. Many people try to just print either the editing or share page to get a PDF. While this dones;t make much sense, I do get a ton of issues about it. So now if you try to do this, it'll just bring you imediately to the print page. Everybody wins!
|
- Override Ctrl+P (and cmd+P) to launch to the print page. Many people try to just print either the editing or share page to get a PDF. While this dones;t make much sense, I do get a ton of issues about it. So now if you try to do this, it'll just bring you imediately to the print page. Everybody wins!
|
||||||
- The onboarding flow has also been confusing a few users (Homepage -> new -> save -> edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
|
- The onboarding flow has also been confusing a few users (Homepage -> new -> save -> edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
|
||||||
|
|
||||||
|
\page
|
||||||
|
|
||||||
- Added a 'Recently Edited' and 'Recently Viewed' nav item to the edit and share page respectively. Each will remember the last 8 items you edited or viewed and when you viewed it. Makes use of the new title attribute of brews to easy navigatation.
|
- Added a 'Recently Edited' and 'Recently Viewed' nav item to the edit and share page respectively. Each will remember the last 8 items you edited or viewed and when you viewed it. Makes use of the new title attribute of brews to easy navigatation.
|
||||||
- Paragraphs now indent properly after lists (thanks u/slitjen!)
|
- Paragraphs now indent properly after lists (thanks u/slitjen!)
|
||||||
|
|
||||||
@@ -171,8 +185,6 @@ All brews made previous to the release of v3.0.0 will still render normally.
|
|||||||
- Bumped up the allowed entity size for extra-large brew (Thanks for reporting it dickboner93)
|
- Bumped up the allowed entity size for extra-large brew (Thanks for reporting it dickboner93)
|
||||||
- Added a little error box when a save fails with a custom link to reporting the issue on github.
|
- Added a little error box when a save fails with a custom link to reporting the issue on github.
|
||||||
|
|
||||||
\page
|
|
||||||
|
|
||||||
### Saturday, 14/05/2016 - v2.0.0 (finally!)
|
### Saturday, 14/05/2016 - v2.0.0 (finally!)
|
||||||
|
|
||||||
I've been working on v2 for a *very* long time. I want to thank you guys for being paitent.
|
I've been working on v2 for a *very* long time. I want to thank you guys for being paitent.
|
||||||
@@ -212,8 +224,6 @@ Massive changelog incoming:
|
|||||||
- Source now opens to it's own route `/source/:sharedId` instead of just a window. Now easier to share, and won't be blocked by some browsers.
|
- Source now opens to it's own route `/source/:sharedId` instead of just a window. Now easier to share, and won't be blocked by some browsers.
|
||||||
- Print page now auto-opens print dialog. If you want to share your print page link, just remove the `?dialog=true` parameter and it won't open the dialog.
|
- Print page now auto-opens print dialog. If you want to share your print page link, just remove the `?dialog=true` parameter and it won't open the dialog.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\page
|
\page
|
||||||
|
|
||||||
### Wednesday, 20/04/2016
|
### Wednesday, 20/04/2016
|
||||||
@@ -281,4 +291,3 @@ Massive changelog incoming:
|
|||||||
* Added `phb.standalone.css` plus a build system for creating it
|
* Added `phb.standalone.css` plus a build system for creating it
|
||||||
* Added page numbers and footer text
|
* Added page numbers and footer text
|
||||||
* Page accent now flips each page
|
* Page accent now flips each page
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./admin.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./brewCleanup.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./brewCompress.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./brewLookup.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./stats.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./brewRenderer.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./errorBar.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
require('./notificationPopup.less');
|
||||||
const React = require('react');
|
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'); //Unused variable
|
const cx = require('classnames'); //Unused variable
|
||||||
|
|
||||||
const DISMISS_KEY = 'dismiss_notification7-24-19';
|
const DISMISS_KEY = 'dismiss_notification7-10-20';
|
||||||
|
|
||||||
const NotificationPopup = createClass({
|
const NotificationPopup = createClass({
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
@@ -22,17 +22,22 @@ const NotificationPopup = createClass({
|
|||||||
notifications : {
|
notifications : {
|
||||||
psa : function(){
|
psa : function(){
|
||||||
return <li key='psa'>
|
return <li key='psa'>
|
||||||
<em>Known bug: Grey Shadow Boxes </em> <br />
|
<em>Google Drive Integration!</em> <br />
|
||||||
The shadows around certain brew elements such as notes and statblocks might appear as a solid grey box when generating a PDF.
|
We have added Google Drive integration to the Homebrewery! <a target='_blank' href='http://naturalcrit.com/login'>Sign in</a> with
|
||||||
<a target='_blank' href='https://old.reddit.com/r/homebrewery/comments/ch3v0d/psa_grey_boxesshadows_around_notes_stat_blocks_etc/'>
|
your Google account to link it with your Homebrewery profile. A new button in the Edit page will let you transfer your file to your personal
|
||||||
See this Reddit post
|
Google Drive storage, and Google will keep a backup of each version! No more lost work surprises!
|
||||||
</a> for updates and possible workarounds.
|
<br /><br />
|
||||||
|
However, we are aware that there may be uncaught bugs. We encourage you to copy your brew into a text document before transferring to Google
|
||||||
|
Drive just in case any issues arise as this update is rolled out.
|
||||||
|
<br /><br />
|
||||||
|
<b>Note:</b> Transferring an existing brew to Google Drive will change the edit and share links of your document. If you have shared your
|
||||||
|
document online, remember to update the links there as well.
|
||||||
</li>;
|
</li>;
|
||||||
},
|
},
|
||||||
faq : function(){
|
faq : function(){
|
||||||
return <li key='faq'>
|
return <li key='faq'>
|
||||||
<em>Protect your work! </em> <br />
|
<em>Protect your work! </em> <br />
|
||||||
At the moment we do not save a history of your projects, so please make frequent backups of your brews!
|
If you opt not to use your Google Drive, keep in mind that we do not save a history of your projects. Please make frequent backups of your brews!
|
||||||
<a target='_blank' href='https://www.reddit.com/r/homebrewery/comments/adh6lh/faqs_psas_announcements/'>
|
<a target='_blank' href='https://www.reddit.com/r/homebrewery/comments/adh6lh/faqs_psas_announcements/'>
|
||||||
See the FAQ
|
See the FAQ
|
||||||
</a> to learn how to avoid losing your work!
|
</a> to learn how to avoid losing your work!
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
float : right;
|
float : right;
|
||||||
display : inline-block;
|
display : inline-block;
|
||||||
width : 350px;
|
width : 350px;
|
||||||
padding : 20px;
|
padding : 15px;
|
||||||
padding-bottom : 10px;
|
padding-bottom : 10px;
|
||||||
padding-left : 85px;
|
padding-left : 55px;
|
||||||
background-color : @blue;
|
background-color : @blue;
|
||||||
color : white;
|
color : white;
|
||||||
a{
|
a{
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
}
|
}
|
||||||
i.info{
|
i.info{
|
||||||
position : absolute;
|
position : absolute;
|
||||||
top : 24px;
|
top : 12px;
|
||||||
left : 24px;
|
left : 12px;
|
||||||
opacity : 0.8;
|
opacity : 0.8;
|
||||||
font-size : 2.5em;
|
font-size : 2.5em;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./editor.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
@@ -138,9 +139,3 @@ const Editor = createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
module.exports = Editor;
|
module.exports = Editor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./metadataEditor.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
@@ -50,7 +51,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 = '/';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./snippetbar.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
BIN
client/homebrew/googleDrive.png
Normal file
BIN
client/homebrew/googleDrive.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 305 KiB |
BIN
client/homebrew/googleDriveMono.png
Normal file
BIN
client/homebrew/googleDriveMono.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1,19 +1,19 @@
|
|||||||
|
require('./homebrew.less');
|
||||||
const React = require('react');
|
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 { StaticRouter:Router, Switch, Route } = require('react-router-dom');
|
||||||
const CreateRouter = require('pico-router').createRouter;
|
const queryString = require('query-string');
|
||||||
|
|
||||||
const HomePage = require('./pages/homePage/homePage.jsx');
|
const HomePage = require('./pages/homePage/homePage.jsx');
|
||||||
const EditPage = require('./pages/editPage/editPage.jsx');
|
const EditPage = require('./pages/editPage/editPage.jsx');
|
||||||
const UserPage = require('./pages/userPage/userPage.jsx');
|
const UserPage = require('./pages/userPage/userPage.jsx');
|
||||||
const SharePage = require('./pages/sharePage/sharePage.jsx');
|
const SharePage = require('./pages/sharePage/sharePage.jsx');
|
||||||
const NewPage = require('./pages/newPage/newPage.jsx');
|
const NewPage = require('./pages/newPage/newPage.jsx');
|
||||||
const ErrorPage = require('./pages/errorPage/errorPage.jsx');
|
//const ErrorPage = require('./pages/errorPage/errorPage.jsx');
|
||||||
const PrintPage = require('./pages/printPage/printPage.jsx');
|
const PrintPage = require('./pages/printPage/printPage.jsx');
|
||||||
|
|
||||||
let Router;
|
|
||||||
const Homebrew = createClass({
|
const Homebrew = createClass({
|
||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
return {
|
return {
|
||||||
@@ -36,55 +36,36 @@ const Homebrew = createClass({
|
|||||||
global.account = this.props.account;
|
global.account = this.props.account;
|
||||||
global.version = this.props.version;
|
global.version = this.props.version;
|
||||||
|
|
||||||
|
|
||||||
Router = CreateRouter({
|
|
||||||
'/edit/:id' : (args)=>{
|
|
||||||
if(!this.props.brew.editId){
|
|
||||||
return <ErrorPage errorId={args.id}/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <EditPage
|
|
||||||
id={args.id}
|
|
||||||
brew={this.props.brew} />;
|
|
||||||
},
|
|
||||||
|
|
||||||
'/share/:id' : (args)=>{
|
|
||||||
if(!this.props.brew.shareId){
|
|
||||||
return <ErrorPage errorId={args.id}/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <SharePage
|
|
||||||
id={args.id}
|
|
||||||
brew={this.props.brew} />;
|
|
||||||
},
|
|
||||||
'/user/:username' : (args)=>{
|
|
||||||
return <UserPage
|
|
||||||
username={args.username}
|
|
||||||
brews={this.props.brews}
|
|
||||||
/>;
|
|
||||||
},
|
|
||||||
'/print/:id' : (args, query)=>{
|
|
||||||
return <PrintPage brew={this.props.brew} query={query}/>;
|
|
||||||
},
|
|
||||||
'/print' : (args, query)=>{
|
|
||||||
return <PrintPage query={query}/>;
|
|
||||||
},
|
|
||||||
'/new' : (args)=>{
|
|
||||||
return <NewPage />;
|
|
||||||
},
|
|
||||||
'/changelog' : (args)=>{
|
|
||||||
return <SharePage
|
|
||||||
brew={{ title: 'Changelog', text: this.props.changelog }} />;
|
|
||||||
},
|
|
||||||
'*' : <HomePage
|
|
||||||
welcomeText={this.props.welcomeText} />,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
render : function (){
|
render : function (){
|
||||||
return <div className='homebrew'>
|
return (
|
||||||
<Router defaultUrl={this.props.url}/>
|
<Router location={this.props.url}>
|
||||||
</div>;
|
<div className='homebrew'>
|
||||||
|
<Switch>
|
||||||
|
<Route path='/edit/:id' component={(routeProps)=><EditPage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
||||||
|
<Route path='/share/:id' component={(routeProps)=><SharePage id={routeProps.match.params.id} brew={this.props.brew} />}/>
|
||||||
|
<Route path='/user/:username' component={(routeProps)=><UserPage username={routeProps.match.params.username} brews={this.props.brews} />}/>
|
||||||
|
<Route path='/print/:id' component={(routeProps)=><PrintPage brew={this.props.brew} query={queryString.parse(routeProps.location.search)} /> } />
|
||||||
|
<Route path='/print' exact component={(routeProps)=><PrintPage query={queryString.parse(routeProps.location.search)} /> } />
|
||||||
|
<Route path='/new' exact component={NewPage}/>
|
||||||
|
<Route path='/changelog' exact component={()=><SharePage brew={{ title: 'Changelog', text: this.props.changelog }} />}/>
|
||||||
|
<Route path='/' component={()=><HomePage welcomeText={this.props.welcomeText}/>}/>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = Homebrew;
|
module.exports = Homebrew;
|
||||||
|
|
||||||
|
//TODO: Nicer Error page instead of just "cant get that"
|
||||||
|
// '/share/:id' : (args)=>{
|
||||||
|
// if(!this.props.brew.shareId){
|
||||||
|
// return <ErrorPage errorId={args.id}/>;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return <SharePage
|
||||||
|
// id={args.id}
|
||||||
|
// brew={this.props.brew} />;
|
||||||
|
// },
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
@import 'naturalcrit/styles/core.less';
|
@import 'naturalcrit/styles/core.less';
|
||||||
.homebrew{
|
.homebrew{
|
||||||
height : 100%;
|
height : 100%;
|
||||||
|
|||||||
@@ -2,17 +2,33 @@ const React = require('react');
|
|||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
|
||||||
module.exports = function(props){
|
const Account = createClass({
|
||||||
|
|
||||||
|
getInitialState : function() {
|
||||||
|
return {
|
||||||
|
url : ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount : function(){
|
||||||
|
if(typeof window !== 'undefined'){
|
||||||
|
this.setState({
|
||||||
|
url : window.location.href
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function(){
|
||||||
if(global.account){
|
if(global.account){
|
||||||
return <Nav.item href={`/user/${global.account.username}`} color='yellow' icon='fa-user'>
|
return <Nav.item href={`/user/${global.account.username}`} color='yellow' icon='fa-user'>
|
||||||
{global.account.username}
|
{global.account.username}
|
||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
}
|
}
|
||||||
let url = '';
|
|
||||||
if(typeof window !== 'undefined'){
|
return <Nav.item href={`http://naturalcrit.com/login?redirect=${this.state.url}`} color='teal' icon='fa-sign-in'>
|
||||||
url = window.location.href;
|
|
||||||
}
|
|
||||||
return <Nav.item href={`http://naturalcrit.com/login?redirect=${url}`} color='teal' icon='fa-sign-in'>
|
|
||||||
login
|
login
|
||||||
</Nav.item>;
|
</Nav.item>;
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Account;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
require('./navbar.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
const PatreonNavItem = require('./patreon.navitem.jsx');
|
||||||
|
|
||||||
const Navbar = createClass({
|
const Navbar = createClass({
|
||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
@@ -39,7 +41,7 @@ const Navbar = createClass({
|
|||||||
<div>The Homebrewery</div>
|
<div>The Homebrewery</div>
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item>{`v${this.state.ver}`}</Nav.item>
|
<Nav.item>{`v${this.state.ver}`}</Nav.item>
|
||||||
|
<PatreonNavItem />
|
||||||
{/*this.renderChromeWarning()*/}
|
{/*this.renderChromeWarning()*/}
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
@navbarHeight : 28px;
|
@navbarHeight : 28px;
|
||||||
|
@keyframes coloring {
|
||||||
|
//from {color: white;}
|
||||||
|
//to {color: red;}
|
||||||
|
0% {color: pink;}
|
||||||
|
50% {color: pink;}
|
||||||
|
75% {color: red;}
|
||||||
|
100% {color: pink;}
|
||||||
|
}
|
||||||
.homebrew nav{
|
.homebrew nav{
|
||||||
.homebrewLogo{
|
.homebrewLogo{
|
||||||
.animate(color);
|
.animate(color);
|
||||||
@@ -47,11 +55,16 @@
|
|||||||
text-transform : initial;
|
text-transform : initial;
|
||||||
}
|
}
|
||||||
.patreon.navItem{
|
.patreon.navItem{
|
||||||
|
border-left : 1px solid #666;
|
||||||
|
border-right : 1px solid #666;
|
||||||
|
&:hover i {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
i{
|
i{
|
||||||
.animate(color);
|
.animate(color);
|
||||||
&:hover{
|
animation-name: coloring;
|
||||||
color : @red;
|
animation-duration: 2s;
|
||||||
}
|
color: pink;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.recent.navItem{
|
.recent.navItem{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ module.exports = function(props){
|
|||||||
return <Nav.item
|
return <Nav.item
|
||||||
className='patreon'
|
className='patreon'
|
||||||
newTab={true}
|
newTab={true}
|
||||||
href='https://www.patreon.com/stolksdorf'
|
href='https://www.patreon.com/NaturalCrit'
|
||||||
color='green'
|
color='green'
|
||||||
icon='fa-heart'>
|
icon='fa-heart'>
|
||||||
help out
|
help out
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
require('./editPage.less');
|
||||||
const React = require('react');
|
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 request = require('superagent');
|
const request = require('superagent');
|
||||||
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
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');
|
||||||
@@ -18,8 +21,10 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
|||||||
|
|
||||||
const Markdown = require('naturalcrit/markdown.js');
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
const SAVE_TIMEOUT = 3000;
|
const googleDriveActive = require('../../googleDrive.png');
|
||||||
|
const googleDriveInactive = require('../../googleDriveMono.png');
|
||||||
|
|
||||||
|
const SAVE_TIMEOUT = 3000;
|
||||||
|
|
||||||
const EditPage = createClass({
|
const EditPage = createClass({
|
||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
@@ -30,6 +35,7 @@ const EditPage = createClass({
|
|||||||
editId : null,
|
editId : null,
|
||||||
createdAt : null,
|
createdAt : null,
|
||||||
updatedAt : null,
|
updatedAt : null,
|
||||||
|
gDrive : false,
|
||||||
|
|
||||||
title : '',
|
title : '',
|
||||||
description : '',
|
description : '',
|
||||||
@@ -47,13 +53,19 @@ const EditPage = createClass({
|
|||||||
|
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
isPending : false,
|
isPending : false,
|
||||||
|
saveGoogle : this.props.brew.googleId ? true : false,
|
||||||
errors : null,
|
errors : null,
|
||||||
htmlErrors : Markdown.validate(this.props.brew.text),
|
htmlErrors : Markdown.validate(this.props.brew.text),
|
||||||
|
url : ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
savedBrew : null,
|
savedBrew : null,
|
||||||
|
|
||||||
componentDidMount : function(){
|
componentDidMount : function(){
|
||||||
|
this.setState({
|
||||||
|
url : window.location.href
|
||||||
|
});
|
||||||
|
|
||||||
this.trySave();
|
this.trySave();
|
||||||
window.onbeforeunload = ()=>{
|
window.onbeforeunload = ()=>{
|
||||||
if(this.state.isSaving || this.state.isPending){
|
if(this.state.isSaving || this.state.isPending){
|
||||||
@@ -72,13 +84,12 @@ const EditPage = createClass({
|
|||||||
document.removeEventListener('keydown', this.handleControlKeys);
|
document.removeEventListener('keydown', this.handleControlKeys);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
handleControlKeys : function(e){
|
handleControlKeys : function(e){
|
||||||
if(!(e.ctrlKey || e.metaKey)) return;
|
if(!(e.ctrlKey || e.metaKey)) return;
|
||||||
const S_KEY = 83;
|
const S_KEY = 83;
|
||||||
const P_KEY = 80;
|
const P_KEY = 80;
|
||||||
if(e.keyCode == S_KEY) this.save();
|
if(e.keyCode == S_KEY) this.save();
|
||||||
if(e.keyCode == P_KEY) window.open(`/print/${this.props.brew.shareId}?dialog=true`, '_blank').focus();
|
if(e.keyCode == P_KEY) window.open(`/print/${this.processShareId()}?dialog=true`, '_blank').focus();
|
||||||
if(e.keyCode == P_KEY || e.keyCode == S_KEY){
|
if(e.keyCode == P_KEY || e.keyCode == S_KEY){
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -124,7 +135,15 @@ const EditPage = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
save : function(){
|
toggleGoogleStorage : function(){
|
||||||
|
this.setState((prevState)=>({
|
||||||
|
saveGoogle : !prevState.saveGoogle,
|
||||||
|
isSaving : false,
|
||||||
|
errors : null
|
||||||
|
}), ()=>this.trySave());
|
||||||
|
},
|
||||||
|
|
||||||
|
save : async function(){
|
||||||
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
|
if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel();
|
||||||
|
|
||||||
this.setState((prevState)=>({
|
this.setState((prevState)=>({
|
||||||
@@ -133,22 +152,99 @@ const EditPage = createClass({
|
|||||||
htmlErrors : Markdown.validate(prevState.brew.text)
|
htmlErrors : Markdown.validate(prevState.brew.text)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
request
|
const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId);
|
||||||
.put(`/api/update/${this.props.brew.editId}`)
|
|
||||||
|
if(this.state.saveGoogle) {
|
||||||
|
if(transfer) {
|
||||||
|
const res = await request
|
||||||
|
.post('/api/newGoogle/')
|
||||||
.send(this.state.brew)
|
.send(this.state.brew)
|
||||||
.end((err, res)=>{
|
.catch((err)=>{
|
||||||
if(err){
|
console.log(err.status === 401
|
||||||
this.setState({
|
? 'Not signed in!'
|
||||||
errors : err,
|
: 'Error Saving to Google!');
|
||||||
|
this.setState({ errors: err });
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
|
if(!res) { return; }
|
||||||
|
|
||||||
|
console.log('Deleting Local Copy');
|
||||||
|
await request.delete(`/api/${this.state.brew.editId}`)
|
||||||
|
.send()
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error deleting Local Copy');
|
||||||
|
});
|
||||||
|
|
||||||
this.savedBrew = res.body;
|
this.savedBrew = res.body;
|
||||||
this.setState({
|
history.replaceState(null, null, `/edit/${this.savedBrew.googleId}${this.savedBrew.editId}`); //update URL to match doc ID
|
||||||
|
} else {
|
||||||
|
const res = await request
|
||||||
|
.put(`/api/updateGoogle/${this.state.brew.editId}`)
|
||||||
|
.send(this.state.brew)
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err.status === 401
|
||||||
|
? 'Not signed in!'
|
||||||
|
: 'Error Saving to Google!');
|
||||||
|
this.setState({ errors: err });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.savedBrew = res.body;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(transfer) {
|
||||||
|
const res = await request.post('/api')
|
||||||
|
.send(this.state.brew)
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error creating Local Copy');
|
||||||
|
this.setState({ errors: err });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
await request.get(`/api/removeGoogle/${this.state.brew.googleId}${this.state.brew.editId}`)
|
||||||
|
.send()
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error Deleting Google Brew');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.savedBrew = res.body;
|
||||||
|
history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); //update URL to match doc ID
|
||||||
|
} else {
|
||||||
|
const res = await request
|
||||||
|
.put(`/api/update/${this.state.brew.editId}`)
|
||||||
|
.send(this.state.brew)
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error Updating Local Brew');
|
||||||
|
this.setState({ errors: err });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.savedBrew = res.body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState((prevState)=>({
|
||||||
|
brew : _.merge({}, prevState.brew, {
|
||||||
|
googleId : this.savedBrew.googleId ? this.savedBrew.googleId : null,
|
||||||
|
editId : this.savedBrew.editId,
|
||||||
|
shareId : this.savedBrew.shareId
|
||||||
|
}),
|
||||||
isPending : false,
|
isPending : false,
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
});
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
renderGoogleDriveIcon : function(){
|
||||||
|
if(this.state.saveGoogle) {
|
||||||
|
return <Nav.item className='googleDriveStorage' onClick={this.toggleGoogleStorage}>
|
||||||
|
<img src={googleDriveActive} alt='googleDriveActive' />
|
||||||
|
</Nav.item>;
|
||||||
|
} else {
|
||||||
|
return <Nav.item className='googleDriveStorage' onClick={this.toggleGoogleStorage}>
|
||||||
|
<img src={googleDriveInactive} alt='googleDriveInactive' />
|
||||||
|
</Nav.item>;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
@@ -159,6 +255,19 @@ const EditPage = createClass({
|
|||||||
errMsg += `\`\`\`\n${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
|
errMsg += `\`\`\`\n${JSON.stringify(this.state.errors.response.error, null, ' ')}\n\`\`\``;
|
||||||
} catch (e){}
|
} catch (e){}
|
||||||
|
|
||||||
|
if(this.state.errors.status == '401'){
|
||||||
|
return <Nav.item className='save error' icon='fa-warning'>
|
||||||
|
Oops!
|
||||||
|
<div className='errorContainer'>
|
||||||
|
You must be signed in to a Google account
|
||||||
|
to save this to Google Drive!<br />
|
||||||
|
Sign in <a target='_blank' rel='noopener noreferrer'
|
||||||
|
href={`http://naturalcrit.com/login?redirect=${this.state.url}`}>
|
||||||
|
here</a>.
|
||||||
|
</div>
|
||||||
|
</Nav.item>;
|
||||||
|
}
|
||||||
|
|
||||||
return <Nav.item className='save error' icon='fa-warning'>
|
return <Nav.item className='save error' icon='fa-warning'>
|
||||||
Oops!
|
Oops!
|
||||||
<div className='errorContainer'>
|
<div className='errorContainer'>
|
||||||
@@ -181,6 +290,13 @@ const EditPage = createClass({
|
|||||||
return <Nav.item className='save saved'>saved.</Nav.item>;
|
return <Nav.item className='save saved'>saved.</Nav.item>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
processShareId : function() {
|
||||||
|
return this.state.brew.googleId ?
|
||||||
|
this.state.brew.googleId + this.state.brew.shareId :
|
||||||
|
this.state.brew.shareId;
|
||||||
|
},
|
||||||
|
|
||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
return <Navbar>
|
return <Navbar>
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
@@ -188,12 +304,13 @@ const EditPage = createClass({
|
|||||||
</Nav.section>
|
</Nav.section>
|
||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
|
{this.renderGoogleDriveIcon()}
|
||||||
{this.renderSaveButton()}
|
{this.renderSaveButton()}
|
||||||
<ReportIssue />
|
<ReportIssue />
|
||||||
<Nav.item newTab={true} href={`/share/${this.props.brew.shareId}`} color='teal' icon='fa-share-alt'>
|
<Nav.item newTab={true} href={`/share/${this.processShareId()}`} color='teal' icon='fa-share-alt'>
|
||||||
Share
|
Share
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<PrintLink shareId={this.props.brew.shareId} />
|
<PrintLink shareId={this.processShareId()} />
|
||||||
<RecentNavItem brew={this.props.brew} storageKey='edit' />
|
<RecentNavItem brew={this.props.brew} storageKey='edit' />
|
||||||
<Account />
|
<Account />
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
@@ -202,6 +319,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'>
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
top : 29px;
|
top : 29px;
|
||||||
left : -20px;
|
left : -20px;
|
||||||
z-index : 1000;
|
z-index : 1000;
|
||||||
width : 120px;
|
width : 135px;
|
||||||
padding : 8px;
|
padding : 6px;
|
||||||
background-color : #333;
|
background-color : #333;
|
||||||
a{
|
a{
|
||||||
color : @teal;
|
color : @teal;
|
||||||
@@ -24,4 +24,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.googleDriveStorage img{
|
||||||
|
height : 20px;
|
||||||
|
padding : 0px;
|
||||||
|
margin : -5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./errorPage.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
require('./homePage.less');
|
||||||
const React = require('react');
|
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 request = require('superagent');
|
const request = require('superagent');
|
||||||
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
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');
|
||||||
const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
|
|
||||||
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
|
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
||||||
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
||||||
@@ -54,7 +55,6 @@ const HomePage = createClass({
|
|||||||
renderNavbar : function(){
|
renderNavbar : function(){
|
||||||
return <Navbar ver={this.props.ver}>
|
return <Navbar ver={this.props.ver}>
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
<PatreonNavItem />
|
|
||||||
<IssueNavItem />
|
<IssueNavItem />
|
||||||
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
|
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
|
||||||
Changelog
|
Changelog
|
||||||
@@ -72,6 +72,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'>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./newPage.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
@@ -23,6 +24,7 @@ const NewPage = createClass({
|
|||||||
getInitialState : function() {
|
getInitialState : function() {
|
||||||
return {
|
return {
|
||||||
metadata : {
|
metadata : {
|
||||||
|
gDrive : false,
|
||||||
title : '',
|
title : '',
|
||||||
description : '',
|
description : '',
|
||||||
tags : '',
|
tags : '',
|
||||||
@@ -33,9 +35,11 @@ const NewPage = createClass({
|
|||||||
|
|
||||||
text : '',
|
text : '',
|
||||||
isSaving : false,
|
isSaving : false,
|
||||||
|
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||||
errors : []
|
errors : []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount : function() {
|
componentDidMount : function() {
|
||||||
const storage = localStorage.getItem(KEY);
|
const storage = localStorage.getItem(KEY);
|
||||||
if(storage){
|
if(storage){
|
||||||
@@ -79,11 +83,29 @@ const NewPage = createClass({
|
|||||||
localStorage.setItem(KEY, text);
|
localStorage.setItem(KEY, text);
|
||||||
},
|
},
|
||||||
|
|
||||||
save : function(){
|
save : async function(){
|
||||||
this.setState({
|
this.setState({
|
||||||
isSaving : true
|
isSaving : true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('saving new brew');
|
||||||
|
|
||||||
|
if(this.state.saveGoogle) {
|
||||||
|
const res = await request
|
||||||
|
.post('/api/newGoogle/')
|
||||||
|
.send(_.merge({}, this.state.metadata, { text: this.state.text }))
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err.status === 401
|
||||||
|
? 'Not signed in!'
|
||||||
|
: 'Error Creating New Google Brew!');
|
||||||
|
this.setState({ isSaving: false });
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
const brew = res.body;
|
||||||
|
localStorage.removeItem(KEY);
|
||||||
|
window.location = `/edit/${brew.googleId}${brew.editId}`;
|
||||||
|
} else {
|
||||||
request.post('/api')
|
request.post('/api')
|
||||||
.send(_.merge({}, this.state.metadata, {
|
.send(_.merge({}, this.state.metadata, {
|
||||||
text : this.state.text
|
text : this.state.text
|
||||||
@@ -100,6 +122,8 @@ const NewPage = createClass({
|
|||||||
localStorage.removeItem(KEY);
|
localStorage.removeItem(KEY);
|
||||||
window.location = `/edit/${brew.editId}`;
|
window.location = `/edit/${brew.editId}`;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderSaveButton : function(){
|
renderSaveButton : function(){
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
require('./printPage.less');
|
||||||
const React = require('react');
|
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/headtags');
|
||||||
const Markdown = require('naturalcrit/markdown.js');
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
const PrintPage = createClass({
|
const PrintPage = createClass({
|
||||||
@@ -42,6 +44,7 @@ const PrintPage = createClass({
|
|||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <div>
|
return <div>
|
||||||
|
<Meta name='robots' content='noindex, nofollow' />
|
||||||
{this.renderPages()}
|
{this.renderPages()}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
require('./sharePage.less');
|
||||||
const React = require('react');
|
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/headtags');
|
||||||
|
|
||||||
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');
|
||||||
@@ -43,16 +45,23 @@ const SharePage = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
processShareId : function() {
|
||||||
|
return this.props.brew.googleId ?
|
||||||
|
this.props.brew.googleId + this.props.brew.shareId :
|
||||||
|
this.props.brew.shareId;
|
||||||
|
},
|
||||||
|
|
||||||
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>
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
|
|
||||||
<Nav.section>
|
<Nav.section>
|
||||||
<PrintLink shareId={this.props.brew.shareId} />
|
<PrintLink shareId={this.processShareId()} />
|
||||||
<Nav.item href={`/source/${this.props.brew.shareId}`} color='teal' icon='fa-code'>
|
<Nav.item href={`/source/${this.processShareId()}`} color='teal' icon='fa-code'>
|
||||||
source
|
source
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<RecentNavItem brew={this.props.brew} storageKey='view' />
|
<RecentNavItem brew={this.props.brew} storageKey='view' />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./brewItem.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
@@ -5,6 +6,8 @@ const cx = require('classnames');
|
|||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const request = require('superagent');
|
const request = require('superagent');
|
||||||
|
|
||||||
|
const googleDriveIcon = require('../../../googleDrive.png');
|
||||||
|
|
||||||
const BrewItem = createClass({
|
const BrewItem = createClass({
|
||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
return {
|
return {
|
||||||
@@ -26,11 +29,19 @@ 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}`)
|
if(this.props.brew.googleId) {
|
||||||
|
request.get(`/api/removeGoogle/${this.props.brew.googleId}${this.props.brew.editId}`)
|
||||||
.send()
|
.send()
|
||||||
.end(function(err, res){
|
.end(function(err, res){
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
request.delete(`/api/${this.props.brew.editId}`)
|
||||||
|
.send()
|
||||||
|
.end(function(err, res){
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
renderDeleteBrewLink : function(){
|
renderDeleteBrewLink : function(){
|
||||||
@@ -40,14 +51,41 @@ const BrewItem = createClass({
|
|||||||
<i className='fa fa-trash' />
|
<i className='fa fa-trash' />
|
||||||
</a>;
|
</a>;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderEditLink : function(){
|
renderEditLink : function(){
|
||||||
if(!this.props.brew.editId) return;
|
if(!this.props.brew.editId) return;
|
||||||
|
|
||||||
return <a href={`/edit/${this.props.brew.editId}`} target='_blank' rel='noopener noreferrer'>
|
let editLink = this.props.brew.editId;
|
||||||
|
if(this.props.brew.googleId) {
|
||||||
|
editLink = this.props.brew.googleId + editLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <a href={`/edit/${editLink}`} target='_blank' rel='noopener noreferrer'>
|
||||||
<i className='fa fa-pencil' />
|
<i className='fa fa-pencil' />
|
||||||
</a>;
|
</a>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderShareLink : function(){
|
||||||
|
if(!this.props.brew.shareId) return;
|
||||||
|
|
||||||
|
let shareLink = this.props.brew.shareId;
|
||||||
|
if(this.props.brew.googleId) {
|
||||||
|
shareLink = this.props.brew.googleId + shareLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <a href={`/share/${shareLink}`} target='_blank' rel='noopener noreferrer'>
|
||||||
|
<i className='fa fa-share-alt' />
|
||||||
|
</a>;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderGoogleDriveIcon : function(){
|
||||||
|
if(!this.props.brew.gDrive) return;
|
||||||
|
|
||||||
|
return <span>
|
||||||
|
<img className='googleDriveIcon' src={googleDriveIcon} alt='googleDriveIcon' />
|
||||||
|
</span>;
|
||||||
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
const brew = this.props.brew;
|
const brew = this.props.brew;
|
||||||
return <div className='brewItem'>
|
return <div className='brewItem'>
|
||||||
@@ -65,12 +103,11 @@ const BrewItem = createClass({
|
|||||||
<span>
|
<span>
|
||||||
<i className='fa fa-refresh' /> {moment(brew.updatedAt).fromNow()}
|
<i className='fa fa-refresh' /> {moment(brew.updatedAt).fromNow()}
|
||||||
</span>
|
</span>
|
||||||
|
{this.renderGoogleDriveIcon()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='links'>
|
<div className='links'>
|
||||||
<a href={`/share/${brew.shareId}`} target='_blank' rel='noopener noreferrer'>
|
{this.renderShareLink()}
|
||||||
<i className='fa fa-share-alt' />
|
|
||||||
</a>
|
|
||||||
{this.renderEditLink()}
|
{this.renderEditLink()}
|
||||||
{this.renderDeleteBrewLink()}
|
{this.renderDeleteBrewLink()}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
box-sizing : border-box;
|
box-sizing : border-box;
|
||||||
overflow : hidden;
|
overflow : hidden;
|
||||||
width : 48%;
|
width : 48%;
|
||||||
|
min-height : 80px;
|
||||||
margin-right : 15px;
|
margin-right : 15px;
|
||||||
margin-bottom : 15px;
|
margin-bottom : 15px;
|
||||||
padding : 5px 15px 5px 8px;
|
padding : 5px 15px 5px 8px;
|
||||||
@@ -55,6 +56,14 @@
|
|||||||
&:hover{
|
&:hover{
|
||||||
opacity : 1;
|
opacity : 1;
|
||||||
}
|
}
|
||||||
|
i{
|
||||||
|
cursor : pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.googleDriveIcon {
|
||||||
|
height : 20px;
|
||||||
|
padding : 0px;
|
||||||
|
margin : -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./userPage.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
@@ -22,7 +23,8 @@ const UserPage = createClass({
|
|||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
return {
|
return {
|
||||||
username : '',
|
username : '',
|
||||||
brews : []
|
brews : [],
|
||||||
|
googleBrews : []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
@import (less) 'shared/naturalcrit/styles/reset.less';
|
@import (less) 'shared/naturalcrit/styles/reset.less';
|
||||||
@import (less) './client/homebrew/phbStyle/phb.fonts.css';
|
@import (less) './client/homebrew/phbStyle/phb.fonts.css';
|
||||||
@import (less) './client/homebrew/phbStyle/phb.assets.less';
|
@import (less) './client/homebrew/phbStyle/phb.assets.less';
|
||||||
@@ -340,7 +339,6 @@ body {
|
|||||||
-webkit-column-break-inside : avoid;
|
-webkit-column-break-inside : avoid;
|
||||||
page-break-inside : avoid;
|
page-break-inside : avoid;
|
||||||
break-inside : avoid;
|
break-inside : avoid;
|
||||||
overflow: hidden; /* Firefox fix */
|
|
||||||
}
|
}
|
||||||
//Better spacing for spell blocks
|
//Better spacing for spell blocks
|
||||||
h4+p+hr+ul{
|
h4+p+hr+ul{
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
module.exports = function(vitreum){
|
module.exports = async (name, props={})=>{
|
||||||
return `
|
return `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
|
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
|
||||||
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
||||||
|
<link href=${`/${name}/bundle.css`} rel='stylesheet'></link>
|
||||||
<link rel="icon" href="/assets/homebrew/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="/assets/homebrew/favicon.ico" type="image/x-icon" />
|
||||||
<title>The Homebrewery - NaturalCrit</title>
|
<title>The Homebrewery - NaturalCrit</title>
|
||||||
${vitreum.head}
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main id="reactRoot">${vitreum.body}</main>
|
<main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main>
|
||||||
</body>
|
</body>
|
||||||
${vitreum.js}
|
<script src=${`/${name}/bundle.js`}></script>
|
||||||
|
<script>start_app(${JSON.stringify(props)})</script>
|
||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
9785
package-lock.json
generated
9785
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
46
package.json
46
package.json
@@ -1,7 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||||
"version": "2.8.2",
|
"version": "2.10.1",
|
||||||
|
"engines": {
|
||||||
|
"node": "12.16.x"
|
||||||
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/naturalcrit/homebrewery.git"
|
"url": "git://github.com/naturalcrit/homebrewery.git"
|
||||||
@@ -9,7 +12,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev.js",
|
"dev": "node scripts/dev.js",
|
||||||
"quick": "node scripts/quick.js",
|
"quick": "node scripts/quick.js",
|
||||||
"build": "node scripts/build.js",
|
"build": "node scripts/buildHomebrew.js",
|
||||||
|
"buildall": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
||||||
"lint": "eslint --fix **/*.{js,jsx}",
|
"lint": "eslint --fix **/*.{js,jsx}",
|
||||||
"lint:dry": "eslint **/*.{js,jsx}",
|
"lint:dry": "eslint **/*.{js,jsx}",
|
||||||
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
||||||
@@ -18,7 +22,7 @@
|
|||||||
"test:dev": "pico-check -v -w",
|
"test:dev": "pico-check -v -w",
|
||||||
"phb": "node scripts/phb.js",
|
"phb": "node scripts/phb.js",
|
||||||
"prod": "set NODE_ENV=production && npm run build",
|
"prod": "set NODE_ENV=production && npm run build",
|
||||||
"postinstall": "npm run build",
|
"postinstall": "npm run buildall",
|
||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"author": "stolksdorf",
|
"author": "stolksdorf",
|
||||||
@@ -36,30 +40,36 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-preset-env": "^1.7.0",
|
"@babel/core": "^7.11.4",
|
||||||
"babel-preset-react": "^6.24.1",
|
"@babel/preset-env": "^7.11.0",
|
||||||
|
"@babel/preset-react": "^7.10.4",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"codemirror": "^5.52.0",
|
"codemirror": "^5.57.0",
|
||||||
"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",
|
||||||
|
"fs-extra": "9.0.1",
|
||||||
|
"googleapis": "59.0.0",
|
||||||
"jwt-simple": "^0.5.6",
|
"jwt-simple": "^0.5.6",
|
||||||
"lodash": "^4.17.15",
|
"less": "^3.12.2",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
"marked": "^0.3.19",
|
"marked": "^0.3.19",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.27.0",
|
||||||
"mongoose": "^5.9.2",
|
"mongoose": "^5.10.0",
|
||||||
|
"nanoid": "3.1.12",
|
||||||
"nconf": "^0.10.0",
|
"nconf": "^0.10.0",
|
||||||
"pico-router": "^2.1.0",
|
"prop-types": "15.7.2",
|
||||||
"react": "^16.13.0",
|
"query-string": "6.13.1",
|
||||||
"react-dom": "^16.13.0",
|
"react": "^16.13.1",
|
||||||
"shortid": "^2.2.15",
|
"react-dom": "^16.13.1",
|
||||||
"superagent": "^5.2.2",
|
"react-router-dom": "5.2.0",
|
||||||
"vitreum": "^4.10.1"
|
"superagent": "^6.0.0",
|
||||||
|
"vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^7.7.0",
|
||||||
"eslint-plugin-react": "^7.19.0",
|
"eslint-plugin-react": "^7.20.6",
|
||||||
"pico-check": "^1.3.2"
|
"pico-check": "^1.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
robots.txt
Normal file
2
robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Notes
|
||||||
|
User-agent: *
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
const label = 'build';
|
|
||||||
console.time(label);
|
|
||||||
|
|
||||||
const clean = require('vitreum/steps/clean.js');
|
|
||||||
const jsx = require('vitreum/steps/jsx.js');
|
|
||||||
const lib = require('vitreum/steps/libs.js');
|
|
||||||
const less = require('vitreum/steps/less.js');
|
|
||||||
const asset = require('vitreum/steps/assets.js');
|
|
||||||
|
|
||||||
const Proj = require('./project.json');
|
|
||||||
|
|
||||||
clean()
|
|
||||||
.then(lib(Proj.libs))
|
|
||||||
.then(()=>jsx('homebrew', './client/homebrew/homebrew.jsx', { libs: Proj.libs, shared: ['./shared'] }))
|
|
||||||
.then((deps)=>less('homebrew', { shared: ['./shared'] }, deps))
|
|
||||||
.then(()=>jsx('admin', './client/admin/admin.jsx', { libs: Proj.libs, shared: ['./shared'] }))
|
|
||||||
.then((deps)=>less('admin', { shared: ['./shared'] }, deps))
|
|
||||||
.then(()=>asset(Proj.assets, ['./shared', './client']))
|
|
||||||
.then(console.timeEnd.bind(console, label))
|
|
||||||
.catch(console.error);
|
|
||||||
31
scripts/buildAdmin.js
Normal file
31
scripts/buildAdmin.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const Proj = require('./project.json');
|
||||||
|
|
||||||
|
const { pack } = require('vitreum');
|
||||||
|
const isDev = !!process.argv.find((arg)=>arg=='--dev');
|
||||||
|
|
||||||
|
const lessTransform = require('vitreum/transforms/less.js');
|
||||||
|
const assetTransform = require('vitreum/transforms/asset.js');
|
||||||
|
//const Meta = require('vitreum/headtags');
|
||||||
|
|
||||||
|
const transforms = {
|
||||||
|
'.less' : lessTransform,
|
||||||
|
'*' : assetTransform('./build')
|
||||||
|
};
|
||||||
|
|
||||||
|
const build = async ({ bundle, render, ssr })=>{
|
||||||
|
await fs.outputFile('./build/admin/bundle.css', await lessTransform.generate({ paths: './shared' }));
|
||||||
|
await fs.outputFile('./build/admin/bundle.js', bundle);
|
||||||
|
await fs.outputFile('./build/admin/ssr.js', ssr);
|
||||||
|
await fs.outputFile('./build/admin/render.js', render);
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.emptyDirSync('./build/admin');
|
||||||
|
pack('./client/admin/admin.jsx', {
|
||||||
|
paths : ['./shared'],
|
||||||
|
libs : Proj.libs,
|
||||||
|
dev : isDev && build,
|
||||||
|
transforms
|
||||||
|
})
|
||||||
|
.then(build)
|
||||||
|
.catch(console.error);
|
||||||
40
scripts/buildHomebrew.js
Normal file
40
scripts/buildHomebrew.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const Proj = require('./project.json');
|
||||||
|
|
||||||
|
const { pack, watchFile, livereload } = require('vitreum');
|
||||||
|
const isDev = !!process.argv.find((arg)=>arg=='--dev');
|
||||||
|
|
||||||
|
const lessTransform = require('vitreum/transforms/less.js');
|
||||||
|
const assetTransform = require('vitreum/transforms/asset.js');
|
||||||
|
//const Meta = require('vitreum/headtags');
|
||||||
|
|
||||||
|
const transforms = {
|
||||||
|
'.less' : lessTransform,
|
||||||
|
'*' : assetTransform('./build')
|
||||||
|
};
|
||||||
|
|
||||||
|
const build = async ({ bundle, render, ssr })=>{
|
||||||
|
await fs.outputFile('./build/homebrew/bundle.css', await lessTransform.generate({ paths: './shared' }));
|
||||||
|
await fs.outputFile('./build/homebrew/bundle.js', bundle);
|
||||||
|
await fs.outputFile('./build/homebrew/ssr.js', ssr);
|
||||||
|
await fs.outputFile('./build/homebrew/render.js', render);
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.emptyDirSync('./build/homebrew');
|
||||||
|
pack('./client/homebrew/homebrew.jsx', {
|
||||||
|
paths : ['./shared'],
|
||||||
|
libs : Proj.libs,
|
||||||
|
dev : isDev && build,
|
||||||
|
transforms
|
||||||
|
})
|
||||||
|
.then(build)
|
||||||
|
.catch(console.error);
|
||||||
|
|
||||||
|
|
||||||
|
//In development set up a watch server and livereload
|
||||||
|
if(isDev){
|
||||||
|
livereload('./build');
|
||||||
|
watchFile('./server.js', {
|
||||||
|
watch : ['./homebrew'] // Watch additional folders if you want
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
"codemirror/mode/javascript/javascript.js",
|
"codemirror/mode/javascript/javascript.js",
|
||||||
"moment",
|
"moment",
|
||||||
"superagent",
|
"superagent",
|
||||||
"marked",
|
"marked"
|
||||||
"pico-router"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
130
server.js
130
server.js
@@ -3,6 +3,9 @@ const jwt = require('jwt-simple');
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
const homebrewApi = require('./server/homebrew.api.js');
|
||||||
|
const GoogleActions = require('./server/googleActions.js');
|
||||||
|
|
||||||
app.use(express.static(`${__dirname}/build`));
|
app.use(express.static(`${__dirname}/build`));
|
||||||
app.use(require('body-parser').json({ limit: '25mb' }));
|
app.use(require('body-parser').json({ limit: '25mb' }));
|
||||||
app.use(require('cookie-parser')());
|
app.use(require('cookie-parser')());
|
||||||
@@ -24,30 +27,57 @@ mongoose.connection.on('error', ()=>{
|
|||||||
throw 'Can not connect to Mongo';
|
throw 'Can not connect to Mongo';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//Account Middleware
|
//Account Middleware
|
||||||
app.use((req, res, next)=>{
|
app.use((req, res, next)=>{
|
||||||
if(req.cookies && req.cookies.nc_session){
|
if(req.cookies && req.cookies.nc_session){
|
||||||
try {
|
try {
|
||||||
req.account = jwt.decode(req.cookies.nc_session, config.get('secret'));
|
req.account = jwt.decode(req.cookies.nc_session, config.get('secret'));
|
||||||
|
//console.log("Just loaded up JWT from cookie:");
|
||||||
|
//console.log(req.account);
|
||||||
} catch (e){}
|
} catch (e){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.config = {
|
||||||
|
google_client_id : config.get('google_client_id'),
|
||||||
|
google_client_secret : config.get('google_client_secret')
|
||||||
|
};
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.use(require('./server/homebrew.api.js'));
|
app.use(homebrewApi);
|
||||||
|
|
||||||
app.use(require('./server/admin.api.js'));
|
app.use(require('./server/admin.api.js'));
|
||||||
|
|
||||||
|
//app.use('/user',require('./server/user.routes.js'));
|
||||||
|
|
||||||
|
|
||||||
const HomebrewModel = require('./server/homebrew.model.js').model;
|
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)=>{
|
||||||
|
if(req.params.id.length > 12) {
|
||||||
|
const googleId = req.params.id.slice(0, -12);
|
||||||
|
const shareId = req.params.id.slice(-12);
|
||||||
|
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
||||||
|
.then((brew)=>{
|
||||||
|
const text = brew.text.replaceAll('<', '<').replaceAll('>', '>');
|
||||||
|
return res.send(`<code><pre style="white-space: pre-wrap;">${text}</pre></code>`);
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err);
|
||||||
|
return res.status(400).send('Can\'t get brew from Google');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
HomebrewModel.get({ shareId: req.params.id })
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
const text = brew.text.replaceAll('<', '<').replaceAll('>', '>');
|
const text = brew.text.replaceAll('<', '<').replaceAll('>', '>');
|
||||||
@@ -57,23 +87,50 @@ app.get('/source/:id', (req, res)=>{
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
return res.status(404).send('Could not find Homebrew with that id');
|
return res.status(404).send('Could not find Homebrew with that id');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//User Page
|
||||||
app.get('/user/:username', (req, res, next)=>{
|
app.get('/user/:username', async (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)
|
|
||||||
.then((brews)=>{
|
let googleBrews = [];
|
||||||
req.brews = brews;
|
|
||||||
|
if(req.account && req.account.googleId){
|
||||||
|
googleBrews = await GoogleActions.listGoogleBrews(req, res)
|
||||||
|
.catch((err)=>{
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const brews = await HomebrewModel.getByUser(req.params.username, fullAccess)
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(googleBrews) {
|
||||||
|
req.brews = _.concat(brews, googleBrews);
|
||||||
|
} else {req.brews = brews;}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Edit Page
|
||||||
|
app.get('/edit/:id', (req, res, next)=>{
|
||||||
|
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
|
||||||
|
if(req.params.id.length > 12) {
|
||||||
|
const googleId = req.params.id.slice(0, -12);
|
||||||
|
const editId = req.params.id.slice(-12);
|
||||||
|
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, editId, 'edit')
|
||||||
|
.then((brew)=>{
|
||||||
|
req.brew = brew; //TODO Need to sanitize later
|
||||||
return next();
|
return next();
|
||||||
})
|
})
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
return res.status(400).send('Can\'t get brew from Google');
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
|
|
||||||
|
|
||||||
app.get('/edit/:id', (req, res, next)=>{
|
|
||||||
HomebrewModel.get({ editId: req.params.id })
|
HomebrewModel.get({ editId: req.params.id })
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
req.brew = brew.sanatize();
|
req.brew = brew.sanatize();
|
||||||
@@ -83,10 +140,24 @@ app.get('/edit/:id', (req, res, next)=>{
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
return res.status(400).send(`Can't get that`);
|
return res.status(400).send(`Can't get that`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Share Page
|
//Share Page
|
||||||
app.get('/share/:id', (req, res, next)=>{
|
app.get('/share/:id', (req, res, next)=>{
|
||||||
|
if(req.params.id.length > 12) {
|
||||||
|
const googleId = req.params.id.slice(0, -12);
|
||||||
|
const shareId = req.params.id.slice(-12);
|
||||||
|
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
||||||
|
.then((brew)=>{
|
||||||
|
req.brew = brew; //TODO Need to sanitize later
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err);
|
||||||
|
return res.status(400).send('Can\'t get brew from Google');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
HomebrewModel.get({ shareId: req.params.id })
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
return brew.increaseView();
|
return brew.increaseView();
|
||||||
@@ -99,10 +170,24 @@ app.get('/share/:id', (req, res, next)=>{
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
return res.status(400).send(`Can't get that`);
|
return res.status(400).send(`Can't get that`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Print Page
|
//Print Page
|
||||||
app.get('/print/:id', (req, res, next)=>{
|
app.get('/print/:id', (req, res, next)=>{
|
||||||
|
if(req.params.id.length > 12) {
|
||||||
|
const googleId = req.params.id.slice(0, -12);
|
||||||
|
const shareId = req.params.id.slice(-12);
|
||||||
|
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
||||||
|
.then((brew)=>{
|
||||||
|
req.brew = brew; //TODO Need to sanitize later
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err);
|
||||||
|
return res.status(400).send('Can\'t get brew from Google');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
HomebrewModel.get({ shareId: req.params.id })
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
req.brew = brew.sanatize(true);
|
req.brew = brew.sanatize(true);
|
||||||
@@ -112,25 +197,30 @@ app.get('/print/:id', (req, res, next)=>{
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
return res.status(400).send(`Can't get that`);
|
return res.status(400).send(`Can't get that`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/source/:id', (req, res)=>{
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//Render Page
|
//Render the page
|
||||||
const render = require('vitreum/steps/render');
|
//const render = require('.build/render');
|
||||||
const templateFn = require('./client/template.js');
|
const templateFn = require('./client/template.js');
|
||||||
app.use((req, res)=>{
|
app.use((req, res)=>{
|
||||||
render('homebrew', templateFn, {
|
const props = {
|
||||||
version : require('./package.json').version,
|
version : require('./package.json').version,
|
||||||
url : req.originalUrl,
|
url : req.originalUrl,
|
||||||
welcomeText : welcomeText,
|
welcomeText : welcomeText,
|
||||||
changelog : changelogText,
|
changelog : changelogText,
|
||||||
brew : req.brew,
|
brew : req.brew,
|
||||||
brews : req.brews,
|
brews : req.brews,
|
||||||
account : req.account
|
googleBrews : req.googleBrews,
|
||||||
})
|
account : req.account,
|
||||||
.then((page)=>{
|
};
|
||||||
return res.send(page);
|
templateFn('homebrew', props)
|
||||||
})
|
.then((page)=>{res.send(page);})
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return res.sendStatus(500);
|
return res.sendStatus(500);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const HomebrewModel = require('./homebrew.model.js').model;
|
const HomebrewModel = require('./homebrew.model.js').model;
|
||||||
const router = require('express').Router();
|
const router = require('express').Router();
|
||||||
const Moment = require('moment');
|
const Moment = require('moment');
|
||||||
const render = require('vitreum/steps/render');
|
//const render = require('vitreum/steps/render');
|
||||||
const templateFn = require('../client/template.js');
|
const templateFn = require('../client/template.js');
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ router.get('/admin/stats', mw.adminOnly, (req, res)=>{
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.get('/admin', mw.adminOnly, (req, res)=>{
|
router.get('/admin', mw.adminOnly, (req, res)=>{
|
||||||
render('admin', templateFn, {
|
templateFn('admin', {
|
||||||
url : req.originalUrl
|
url : req.originalUrl
|
||||||
})
|
})
|
||||||
.then((page)=>res.send(page))
|
.then((page)=>res.send(page))
|
||||||
|
|||||||
@@ -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}`);
|
||||||
}
|
}
|
||||||
|
|||||||
312
server/googleActions.js
Normal file
312
server/googleActions.js
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
const _ = require('lodash');
|
||||||
|
const { google } = require('googleapis');
|
||||||
|
const { nanoid } = require('nanoid');
|
||||||
|
const token = require('./token.js');
|
||||||
|
const config = require('nconf')
|
||||||
|
.argv()
|
||||||
|
.env({ lowerCase: true }) // Load environment variables
|
||||||
|
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
|
||||||
|
.file('defaults', { file: 'config/default.json' });
|
||||||
|
|
||||||
|
//let oAuth2Client;
|
||||||
|
|
||||||
|
GoogleActions = {
|
||||||
|
|
||||||
|
authCheck : (account, res)=>{
|
||||||
|
if(!account || !account.googleId){ // If not signed into Google
|
||||||
|
const err = new Error('Not Signed In');
|
||||||
|
err.status = 401;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oAuth2Client = new google.auth.OAuth2(
|
||||||
|
config.get('google_client_id'),
|
||||||
|
config.get('google_client_secret'),
|
||||||
|
'/auth/google/redirect'
|
||||||
|
);
|
||||||
|
|
||||||
|
oAuth2Client.setCredentials({
|
||||||
|
access_token : account.googleAccessToken, //Comment out to refresh token
|
||||||
|
refresh_token : account.googleRefreshToken
|
||||||
|
});
|
||||||
|
|
||||||
|
oAuth2Client.on('tokens', (tokens)=>{
|
||||||
|
if(tokens.refresh_token) {
|
||||||
|
account.googleRefreshToken = tokens.refresh_token;
|
||||||
|
}
|
||||||
|
account.googleAccessToken = tokens.access_token;
|
||||||
|
const JWTToken = token.generateAccessToken(account);
|
||||||
|
|
||||||
|
//Save updated token to cookie
|
||||||
|
//res.cookie('nc_session', JWTToken, { maxAge: 1000*60*60*24*365, path: '/', sameSite: 'lax' });
|
||||||
|
res.cookie('nc_session', JWTToken, { maxAge: 1000*60*60*24*365, path: '/', sameSite: 'lax', domain: '.naturalcrit.com' });
|
||||||
|
});
|
||||||
|
|
||||||
|
return oAuth2Client;
|
||||||
|
},
|
||||||
|
|
||||||
|
getGoogleFolder : async (auth)=>{
|
||||||
|
const drive = google.drive({ version: 'v3', auth: auth });
|
||||||
|
|
||||||
|
fileMetadata = {
|
||||||
|
'name' : 'Homebrewery',
|
||||||
|
'mimeType' : 'application/vnd.google-apps.folder'
|
||||||
|
};
|
||||||
|
|
||||||
|
const obj = await drive.files.list({
|
||||||
|
q : 'mimeType = \'application/vnd.google-apps.folder\''
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error searching Google Drive Folders');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
let folderId;
|
||||||
|
|
||||||
|
if(obj.data.files.length == 0){
|
||||||
|
const obj = await drive.files.create({
|
||||||
|
resource : fileMetadata
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error creating google app folder');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('created new drive folder with ID:');
|
||||||
|
console.log(obj.data.id);
|
||||||
|
folderId = obj.data.id;
|
||||||
|
} else {
|
||||||
|
folderId = obj.data.files[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderId;
|
||||||
|
},
|
||||||
|
|
||||||
|
listGoogleBrews : async (req, res)=>{
|
||||||
|
|
||||||
|
oAuth2Client = GoogleActions.authCheck(req.account, res);
|
||||||
|
|
||||||
|
const drive = google.drive({ version: 'v3', auth: oAuth2Client });
|
||||||
|
|
||||||
|
const obj = await drive.files.list({
|
||||||
|
pageSize : 100,
|
||||||
|
fields : 'nextPageToken, files(id, name, modifiedTime, properties)',
|
||||||
|
q : 'mimeType != \'application/vnd.google-apps.folder\' and trashed = false'
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
return console.error(`Error Listing Google Brews: ${err}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!obj.data.files.length) {
|
||||||
|
console.log('No files found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const brews = obj.data.files.map((file)=>{
|
||||||
|
return {
|
||||||
|
text : '',
|
||||||
|
shareId : file.properties.shareId,
|
||||||
|
editId : file.properties.editId,
|
||||||
|
createdAt : null,
|
||||||
|
updatedAt : file.modifiedTime,
|
||||||
|
gDrive : true,
|
||||||
|
googleId : file.id,
|
||||||
|
|
||||||
|
title : file.properties.title,
|
||||||
|
description : '',
|
||||||
|
tags : '',
|
||||||
|
published : false,
|
||||||
|
authors : [req.account.username], //TODO: properly save and load authors to google drive
|
||||||
|
systems : []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return brews;
|
||||||
|
},
|
||||||
|
|
||||||
|
existsGoogleBrew : async (auth, id)=>{
|
||||||
|
const drive = google.drive({ version: 'v3', auth: auth });
|
||||||
|
|
||||||
|
const result = await drive.files.get({ fileId: id })
|
||||||
|
.catch((err)=>{
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(result){return true;}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateGoogleBrew : async (auth, brew)=>{
|
||||||
|
const drive = google.drive({ version: 'v3', auth: auth });
|
||||||
|
|
||||||
|
if(await GoogleActions.existsGoogleBrew(auth, brew.googleId) == true) {
|
||||||
|
|
||||||
|
await drive.files.update({
|
||||||
|
fileId : brew.googleId,
|
||||||
|
resource : { name : `${brew.title}.txt`,
|
||||||
|
properties : { title: brew.title } //AppProperties is not accessible via API key
|
||||||
|
},
|
||||||
|
media : { mimeType : 'text/plain',
|
||||||
|
body : brew.text }
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error saving to google');
|
||||||
|
console.error(err);
|
||||||
|
//return res.status(500).send('Error while saving');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (brew);
|
||||||
|
},
|
||||||
|
|
||||||
|
newGoogleBrew : async (auth, brew)=>{
|
||||||
|
const drive = google.drive({ version: 'v3', auth: auth });
|
||||||
|
|
||||||
|
const media = {
|
||||||
|
mimeType : 'text/plain',
|
||||||
|
body : brew.text
|
||||||
|
};
|
||||||
|
|
||||||
|
const folderId = await GoogleActions.getGoogleFolder(auth);
|
||||||
|
|
||||||
|
const fileMetadata = {
|
||||||
|
'name' : `${brew.title}.txt`,
|
||||||
|
'parents' : [folderId],
|
||||||
|
'properties' : { //AppProperties is not accessible
|
||||||
|
'shareId' : nanoid(12),
|
||||||
|
'editId' : nanoid(12),
|
||||||
|
'title' : brew.title,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const obj = await drive.files.create({
|
||||||
|
resource : fileMetadata,
|
||||||
|
media : media
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).send('Error while creating google brew');
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!obj) return;
|
||||||
|
|
||||||
|
await drive.permissions.create({
|
||||||
|
resource : { type : 'anyone',
|
||||||
|
role : 'writer' },
|
||||||
|
fileId : obj.data.id,
|
||||||
|
fields : 'id',
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error updating permissions');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
const newHomebrew = {
|
||||||
|
text : brew.text,
|
||||||
|
shareId : fileMetadata.properties.shareId,
|
||||||
|
editId : fileMetadata.properties.editId,
|
||||||
|
createdAt : null,
|
||||||
|
updatedAt : null,
|
||||||
|
gDrive : true,
|
||||||
|
googleId : obj.data.id,
|
||||||
|
|
||||||
|
title : brew.title,
|
||||||
|
description : '',
|
||||||
|
tags : '',
|
||||||
|
published : false,
|
||||||
|
authors : [],
|
||||||
|
systems : []
|
||||||
|
};
|
||||||
|
|
||||||
|
return newHomebrew;
|
||||||
|
},
|
||||||
|
|
||||||
|
readFileMetadata : async (auth, id, accessId, accessType)=>{
|
||||||
|
const drive = google.drive({ version: 'v3', auth: auth });
|
||||||
|
console.log(auth);
|
||||||
|
|
||||||
|
const obj = await drive.files.get({
|
||||||
|
fileId : id,
|
||||||
|
fields : 'properties'
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error loading from Google');
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`ACCESS TYPE: ${accessType}`);
|
||||||
|
|
||||||
|
if(obj) {
|
||||||
|
if(accessType == 'edit' && obj.data.properties.editId != accessId){
|
||||||
|
throw ('Edit ID does not match');
|
||||||
|
} else if(accessType == 'share' && obj.data.properties.shareId != accessId){
|
||||||
|
throw ('Share ID does not match');
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = await drive.files.get({
|
||||||
|
fileId : id,
|
||||||
|
alt : 'media'
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error getting file contents from Google');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
const brew = {
|
||||||
|
text : file.data,
|
||||||
|
shareId : obj.data.properties.shareId,
|
||||||
|
editId : obj.data.properties.editId,
|
||||||
|
createdAt : null,
|
||||||
|
updatedAt : null,
|
||||||
|
gDrive : true,
|
||||||
|
googleId : id,
|
||||||
|
|
||||||
|
title : obj.data.properties.title,
|
||||||
|
description : '',
|
||||||
|
tags : '',
|
||||||
|
published : false,
|
||||||
|
authors : [],
|
||||||
|
systems : []
|
||||||
|
};
|
||||||
|
|
||||||
|
return (brew);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteGoogleBrew : async (req, res, id)=>{
|
||||||
|
|
||||||
|
oAuth2Client = GoogleActions.authCheck(req.account, res);
|
||||||
|
const drive = google.drive({ version: 'v3', auth: oAuth2Client });
|
||||||
|
|
||||||
|
const googleId = id.slice(0, -12);
|
||||||
|
const accessId = id.slice(-12);
|
||||||
|
|
||||||
|
const obj = await drive.files.get({
|
||||||
|
fileId : googleId,
|
||||||
|
fields : 'properties'
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error loading from Google');
|
||||||
|
console.error(err);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(obj && obj.data.properties.editId != accessId) {
|
||||||
|
throw ('Not authorized to delete this Google brew');
|
||||||
|
}
|
||||||
|
|
||||||
|
await drive.files.delete({
|
||||||
|
fileId : googleId
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Can\'t delete Google file');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = GoogleActions;
|
||||||
@@ -2,6 +2,7 @@ const _ = require('lodash');
|
|||||||
const HomebrewModel = require('./homebrew.model.js').model;
|
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 GoogleActions = require('./googleActions.js');
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -15,49 +16,53 @@ const getGoodBrewTitle = (text)=>{
|
|||||||
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 brew = req.body;
|
||||||
|
brew.authors = (req.account) ? [req.account.username] : [];
|
||||||
|
|
||||||
|
if(!brew.title) {
|
||||||
router.post('/api', (req, res)=>{
|
brew.title = getGoodBrewTitle(brew.text);
|
||||||
|
|
||||||
let authors = [];
|
|
||||||
if(req.account) authors = [req.account.username];
|
|
||||||
|
|
||||||
const newHomebrew = new HomebrewModel(_.merge({},
|
|
||||||
req.body,
|
|
||||||
{ authors: authors }
|
|
||||||
));
|
|
||||||
|
|
||||||
if(!newHomebrew.title){
|
|
||||||
newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); // Compress brew text to binary before saving
|
delete brew.editId;
|
||||||
newHomebrew.text = undefined; // Delete the non-binary text field since it's not needed anymore
|
delete brew.shareId;
|
||||||
|
delete brew.googleId;
|
||||||
|
|
||||||
|
const newHomebrew = new HomebrewModel(brew);
|
||||||
|
// Compress brew text to binary before saving
|
||||||
|
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.put('/api/update/:id', (req, res)=>{
|
obj = obj.toObject();
|
||||||
|
obj.gDrive = false;
|
||||||
|
return res.status(200).send(obj);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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,87 +73,83 @@ 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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(brew.authors.length === 0) {
|
||||||
// Delete brew if there are no authors left
|
// Delete brew if there are no authors left
|
||||||
if(!brew.authors.length)
|
|
||||||
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();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
// Otherwise, save the brew with updated author list
|
// Otherwise, save the brew with updated author list
|
||||||
else
|
|
||||||
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);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const newGoogleBrew = async (req, res, next)=>{
|
||||||
|
let oAuth2Client;
|
||||||
|
|
||||||
|
try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); }
|
||||||
|
|
||||||
|
const brew = req.body;
|
||||||
|
brew.authors = (req.account) ? [req.account.username] : [];
|
||||||
|
|
||||||
|
if(!brew.title) {
|
||||||
|
brew.title = getGoodBrewTitle(brew.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete brew.editId;
|
||||||
|
delete brew.shareId;
|
||||||
|
delete brew.googleId;
|
||||||
|
|
||||||
|
req.body = brew;
|
||||||
|
|
||||||
|
console.log(oAuth2Client);
|
||||||
|
|
||||||
|
const newBrew = await GoogleActions.newGoogleBrew(oAuth2Client, brew);
|
||||||
|
|
||||||
|
return res.status(200).send(newBrew);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateGoogleBrew = async (req, res, next)=>{
|
||||||
|
let oAuth2Client;
|
||||||
|
|
||||||
|
try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); }
|
||||||
|
|
||||||
|
const updatedBrew = await GoogleActions.updateGoogleBrew(oAuth2Client, req.body);
|
||||||
|
|
||||||
|
return res.status(200).send(updatedBrew);
|
||||||
|
};
|
||||||
|
|
||||||
|
router.post('/api', newBrew);
|
||||||
|
router.post('/api/newGoogle/', newGoogleBrew);
|
||||||
|
router.put('/api/:id', updateBrew);
|
||||||
|
router.put('/api/update/:id', updateBrew);
|
||||||
|
router.put('/api/updateGoogle/:id', updateGoogleBrew);
|
||||||
|
router.delete('/api/:id', deleteBrew);
|
||||||
|
router.get('/api/remove/:id', deleteBrew);
|
||||||
|
router.get('/api/removeGoogle/:id', (req, res)=>{GoogleActions.deleteGoogleBrew(req, res, req.params.id);});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function(app){
|
|
||||||
|
|
||||||
app;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.get('/api/search', mw.adminOnly, function(req, res){
|
|
||||||
|
|
||||||
var page = req.query.page || 0;
|
|
||||||
var count = req.query.count || 20;
|
|
||||||
|
|
||||||
var query = {};
|
|
||||||
if(req.query && req.query.id){
|
|
||||||
query = {
|
|
||||||
"$or" : [{
|
|
||||||
editId : req.query.id
|
|
||||||
},{
|
|
||||||
shareId : req.query.id
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
HomebrewModel.find(query, {
|
|
||||||
text : 0 //omit the text
|
|
||||||
}, {
|
|
||||||
skip: page*count,
|
|
||||||
limit: count*1
|
|
||||||
}, function(err, objs){
|
|
||||||
if(err) console.log(err);
|
|
||||||
return res.json({
|
|
||||||
page : page,
|
|
||||||
count : count,
|
|
||||||
total : homebrewTotal,
|
|
||||||
brews : objs
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
const shortid = require('shortid');
|
const { nanoid } = require('nanoid');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
|
||||||
const HomebrewSchema = mongoose.Schema({
|
const HomebrewSchema = mongoose.Schema({
|
||||||
shareId : { type: String, default: shortid.generate, index: { unique: true } },
|
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
|
||||||
editId : { type: String, default: shortid.generate, index: { unique: true } },
|
editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
|
||||||
title : { type: String, default: '' },
|
title : { type: String, default: '' },
|
||||||
text : { type: String, default: '' },
|
text : { type: String, default: '' },
|
||||||
textBin : { type: Buffer },
|
textBin : { type: Buffer },
|
||||||
@@ -24,7 +24,6 @@ const HomebrewSchema = mongoose.Schema({
|
|||||||
}, { versionKey: false });
|
}, { versionKey: false });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HomebrewSchema.methods.sanatize = function(full=false){
|
HomebrewSchema.methods.sanatize = function(full=false){
|
||||||
const brew = this.toJSON();
|
const brew = this.toJSON();
|
||||||
delete brew._id;
|
delete brew._id;
|
||||||
@@ -35,7 +34,6 @@ HomebrewSchema.methods.sanatize = function(full=false){
|
|||||||
return brew;
|
return brew;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
HomebrewSchema.methods.increaseView = function(){
|
HomebrewSchema.methods.increaseView = function(){
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
this.lastViewed = new Date();
|
this.lastViewed = new Date();
|
||||||
@@ -47,8 +45,6 @@ HomebrewSchema.methods.increaseView = function(){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HomebrewSchema.statics.get = function(query){
|
HomebrewSchema.statics.get = function(query){
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
Homebrew.find(query, (err, brews)=>{
|
Homebrew.find(query, (err, brews)=>{
|
||||||
@@ -77,8 +73,6 @@ HomebrewSchema.statics.getByUser = function(username, allowAccess=false){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
|
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
33
server/token.js
Normal file
33
server/token.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const jwt = require('jwt-simple');
|
||||||
|
|
||||||
|
// Load configuration values
|
||||||
|
const config = require('nconf')
|
||||||
|
.argv()
|
||||||
|
.env({ lowerCase: true }) // Load environment variables
|
||||||
|
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
|
||||||
|
.file('defaults', { file: 'config/default.json' });
|
||||||
|
|
||||||
|
// Generate an Access Token for the given User ID
|
||||||
|
const generateAccessToken = (account)=>{
|
||||||
|
const payload = account;
|
||||||
|
|
||||||
|
// When the token was issued
|
||||||
|
payload.issued = (new Date());
|
||||||
|
// Which service issued the Token
|
||||||
|
payload.issuer = config.get('authentication_token_issuer');
|
||||||
|
// Which service is the token intended for
|
||||||
|
payload.audience = config.get('authentication_token_audience');
|
||||||
|
// The signing key for signing the token
|
||||||
|
delete payload.password;
|
||||||
|
delete payload._id;
|
||||||
|
|
||||||
|
const secret = config.get('authentication_token_secret');
|
||||||
|
|
||||||
|
const token = jwt.encode(payload, secret);
|
||||||
|
|
||||||
|
return token;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateAccessToken : generateAccessToken
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
|
require('./renderWarnings.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
@@ -25,10 +25,10 @@ const RenderWarnings = createClass({
|
|||||||
if(!isChrome){
|
if(!isChrome){
|
||||||
return <li key='chrome'>
|
return <li key='chrome'>
|
||||||
<em>Built for Chrome </em> <br />
|
<em>Built for Chrome </em> <br />
|
||||||
Other browsers do not support
|
Other browsers have not been tested for compatiblilty. If you
|
||||||
<a target='_blank' href='https://developer.mozilla.org/en-US/docs/Web/CSS/column-span#Browser_compatibility'>
|
experience issues with your document not rendering or printing
|
||||||
key features
|
properly, please try using the latest version of Chrome before
|
||||||
</a> this site uses.
|
submitting a bug report.
|
||||||
</li>;
|
</li>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./codeEditor.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./nav.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//@import (less) 'naturalcrit/styles/style.fonts.css';
|
||||||
nav{
|
nav{
|
||||||
background-color : #333;
|
background-color : #333;
|
||||||
.navContent{
|
.navContent{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
require('./splitPane.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
@import 'naturalcrit/styles/colors.less';
|
@import 'naturalcrit/styles/colors.less';
|
||||||
@import 'naturalcrit/styles/tooltip.less';
|
@import 'naturalcrit/styles/tooltip.less';
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family : CodeLight;
|
font-family : 'CodeLight';
|
||||||
src : url('/assets/naturalcrit/styles/CODE Light.otf');
|
src : data-uri('naturalcrit/styles/CODE Light.otf') format('opentype');
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family : CodeBold;
|
font-family : 'CodeBold';
|
||||||
src : url('/assets/naturalcrit/styles/CODE Bold.otf');
|
src : data-uri('naturalcrit/styles/CODE Bold.otf') format('opentype');
|
||||||
}
|
}
|
||||||
html,body, #reactRoot{
|
html,body, #reactRoot{
|
||||||
height : 100vh;
|
height : 100vh;
|
||||||
|
|||||||
Reference in New Issue
Block a user