0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-24 01:13:15 +00:00

Compare commits

..

105 Commits

Author SHA1 Message Date
Trevor Buckner
825702ee1d Forgot version number 2020-03-11 09:41:16 -04:00
Trevor Buckner
61b58032ca Upping changelog to v2.8.2 2020-03-11 09:40:50 -04:00
Trevor Buckner
546cc13c1c Release 2.8.2
Marking this as a stable version since it's been a while, in preparation for adding new features.
2020-03-11 09:29:04 -04:00
dependabot-preview[bot]
44649d7f51 Bump eslint-plugin-react from 7.18.3 to 7.19.0
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.18.3 to 7.19.0.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.18.3...v7.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-10 12:22:35 -04:00
dependabot-preview[bot]
5f9aaba262 Bump react from 16.12.0 to 16.13.0
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.12.0 to 16.13.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.0/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:21:23 -05:00
dependabot-preview[bot]
5d5c0b9773 Bump codemirror from 5.51.0 to 5.52.0
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.51.0 to 5.52.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.51.0...5.52.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:20:24 -05:00
dependabot-preview[bot]
b681edba23 Bump mongoose from 5.9.1 to 5.9.2
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.9.1 to 5.9.2.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.9.1...5.9.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:19:33 -05:00
dependabot-preview[bot]
f0c412527b Bump react-dom from 16.12.0 to 16.13.0
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.12.0 to 16.13.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.0/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-27 13:17:32 -05:00
dependabot-preview[bot]
fcf6b8d764 Bump babel-preset-env from 1.6.1 to 1.7.0
Bumps [babel-preset-env](https://github.com/babel/babel-preset-env) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/babel/babel-preset-env/releases)
- [Changelog](https://github.com/babel/babel-preset-env/blob/1.x/CHANGELOG.md)
- [Commits](https://github.com/babel/babel-preset-env/compare/v1.6.1...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 10:06:36 -05:00
dependabot-preview[bot]
8607b9dba8 Bump mongoose from 5.8.11 to 5.9.1
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.8.11 to 5.9.1.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.8.11...5.9.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 10:03:20 -05:00
dependabot-preview[bot]
ef4fa89d9a Bump superagent from 5.2.1 to 5.2.2
Bumps [superagent](https://github.com/visionmedia/superagent) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v5.2.1...v5.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-18 10:00:09 -05:00
Trevor Buckner
6d38a633ef Cut out the renderPrivateBrews script 2020-02-07 10:16:58 -05:00
Eric Scheid
b3376435b9 purty apostrophes; containerise brew collections
NB: .brewItem:nth-child(2n+1) {...} counts all children of the contain, not just the .brewItem elements.
This meant if there were an even number of published brews, the style rule got applied to the wrong unpublished brews because there's an <h1> jigging the count.
2020-02-07 10:16:58 -05:00
Trevor Buckner
3259836964 Remove extra |:--:| in table
This is invalid markdown.
2020-02-04 12:06:36 -05:00
Trevor Buckner
b3387c363f Bump react-dom to v16.12.0 2020-02-04 11:40:41 -05:00
Trevor Buckner
f0d9fcf942 Bump eslint version 2020-02-04 11:09:15 -05:00
Trevor Buckner
928b553b19 Revert Marked update
The markdown spec has changed too much and is breaking old brews.
2020-02-04 10:34:00 -05:00
dependabot-preview[bot]
508f87f117 Bump express from 4.16.3 to 4.17.1
Bumps [express](https://github.com/expressjs/express) from 4.16.3 to 4.17.1.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.16.3...4.17.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:29:53 -05:00
dependabot-preview[bot]
03b389761b Bump body-parser from 1.18.2 to 1.19.0
Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.18.2 to 1.19.0.
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.18.2...1.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:16:25 -05:00
dependabot-preview[bot]
36d0f15960 Bump shortid from 2.2.8 to 2.2.15
Bumps [shortid](https://github.com/dylang/shortid) from 2.2.8 to 2.2.15.
- [Release notes](https://github.com/dylang/shortid/releases)
- [Changelog](https://github.com/dylang/shortid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dylang/shortid/compare/2.2.8...2.2.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:09:30 -05:00
dependabot-preview[bot]
b56d4fb773 Bump pico-check from 1.0.3 to 1.3.2
Bumps [pico-check](https://github.com/stolksdorf/pico-check) from 1.0.3 to 1.3.2.
- [Release notes](https://github.com/stolksdorf/pico-check/releases)
- [Commits](https://github.com/stolksdorf/pico-check/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:08:39 -05:00
dependabot-preview[bot]
272b336cd8 Bump cookie-parser from 1.4.3 to 1.4.4
Bumps [cookie-parser](https://github.com/expressjs/cookie-parser) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/expressjs/cookie-parser/releases)
- [Changelog](https://github.com/expressjs/cookie-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/cookie-parser/compare/1.4.3...1.4.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:04:01 -05:00
dependabot-preview[bot]
ac58833adf Bump marked from 0.3.19 to 0.8.0
Bumps [marked](https://github.com/markedjs/marked) from 0.3.19 to 0.8.0.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Commits](https://github.com/markedjs/marked/compare/v0.3.19...v0.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 22:01:03 -05:00
dependabot-preview[bot]
c3432a9263 Bump codemirror from 5.36.0 to 5.51.0
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.36.0 to 5.51.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.36.0...5.51.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 21:44:09 -05:00
dependabot-preview[bot]
f8f1c99266 Bump jwt-simple from 0.5.1 to 0.5.6
Bumps [jwt-simple](https://github.com/hokaccha/node-jwt-simple) from 0.5.1 to 0.5.6.
- [Release notes](https://github.com/hokaccha/node-jwt-simple/releases)
- [Changelog](https://github.com/hokaccha/node-jwt-simple/blob/master/History.md)
- [Commits](https://github.com/hokaccha/node-jwt-simple/compare/v0.5.1...v0.5.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 21:33:42 -05:00
dependabot-preview[bot]
0e9b50d4e8 Bump eslint-plugin-react from 7.7.0 to 7.18.3
Bumps [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react) from 7.7.0 to 7.18.3.
- [Release notes](https://github.com/yannickcr/eslint-plugin-react/releases)
- [Changelog](https://github.com/yannickcr/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yannickcr/eslint-plugin-react/compare/v7.7.0...v7.18.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 16:05:01 -05:00
dependabot-preview[bot]
e6e995d7f4 Bump moment from 2.22.0 to 2.24.0
Bumps [moment](https://github.com/moment/moment) from 2.22.0 to 2.24.0.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.22.0...2.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 16:00:45 -05:00
dependabot-preview[bot]
c9d18be0cb Bump superagent from 3.8.2 to 5.2.1
Bumps [superagent](https://github.com/visionmedia/superagent) from 3.8.2 to 5.2.1.
- [Release notes](https://github.com/visionmedia/superagent/releases)
- [Changelog](https://github.com/visionmedia/superagent/blob/master/HISTORY.md)
- [Commits](https://github.com/visionmedia/superagent/compare/v3.8.2...v5.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 15:37:06 -05:00
Trevor Buckner
b75063f936 Try upping Mongoose to 5.7.5 again
Apparently mongoose 5.7.0 has breaking changes.
2020-02-03 15:29:12 -05:00
Trevor Buckner
6ef88e0f1f Revert "Try upping Mongoose to 5.7.5"
This reverts commit 59e0118d8b.
2020-02-03 14:52:06 -05:00
Trevor Buckner
59e0118d8b Try upping Mongoose to 5.7.5
Small fix to connection string to keep Mongoose working with the older MongoDB 3.6 database
2020-02-03 14:49:03 -05:00
Trevor Buckner
09e6766e0d Update .gitignore 2020-02-03 14:16:06 -05:00
Trevor Buckner
ce8cfde211 Revert "[Security] Bump mongoose from 5.0.13 to 5.7.5"
This reverts commit 7fbb51b3f4.
2020-02-03 14:11:47 -05:00
dependabot-preview[bot]
7fbb51b3f4 [Security] Bump mongoose from 5.0.13 to 5.7.5
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.0.13 to 5.7.5. **This update includes a security fix.**
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.0.13...5.7.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 14:03:23 -05:00
dependabot-preview[bot]
1e9b8e679d Bump lodash from 4.17.13 to 4.17.15
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.13 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.13...4.17.15)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:50:31 -05:00
dependabot-preview[bot]
53a1c4f85d [Security] Bump react-dom from 16.3.1 to 16.3.3
Bumps [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) from 16.3.1 to 16.3.3. **This update includes a security fix.**
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/HEAD/packages/react-dom)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:50:10 -05:00
dependabot-preview[bot]
b1c252495b Bump react from 16.3.1 to 16.12.0
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.3.1 to 16.12.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.12.0/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:47:22 -05:00
dependabot-preview[bot]
5fce35edd7 Bump classnames from 2.2.5 to 2.2.6
Bumps [classnames](https://github.com/JedWatson/classnames) from 2.2.5 to 2.2.6.
- [Release notes](https://github.com/JedWatson/classnames/releases)
- [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md)
- [Commits](https://github.com/JedWatson/classnames/compare/v2.2.5...v2.2.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-03 13:32:02 -05:00
Trevor Buckner
debe58ff0b Remove unneeded comments 2020-01-28 10:53:07 -05:00
Trevor Buckner
0018627f82 Process brew compression in batches to reduce server load. 2020-01-28 10:53:07 -05:00
Trevor Buckner
e50f0a1f3b Upping number of compressed brews at once 2020-01-23 15:12:25 -05:00
Trevor Buckner
e862f65166 Up number of brews compressed at once 2020-01-23 12:57:23 -05:00
Trevor Buckner
347575b0ec Lint 2 2020-01-23 12:24:54 -05:00
Trevor Buckner
4ac922482e Lint 2020-01-23 12:24:54 -05:00
Trevor Buckner
27e7af870a Fixed what was breaking 2020-01-23 12:24:54 -05:00
Trevor Buckner
474b2552fd Fix recent crash 2020-01-23 11:47:21 -05:00
Trevor Buckner
a06b29c6f5 Admin controls for compressing old brews 2020-01-23 11:20:31 -05:00
Trevor Buckner
4128670a9f Revert "Merge pull request #867 from naturalcrit/compressBrews"
This reverts commit 94d090277f, reversing
changes made to aeffec1763.
2020-01-23 10:27:30 -05:00
Trevor Buckner
94d090277f Merge pull request #867 from naturalcrit/compressBrews
Adding some admin controls to compress old brews.
2020-01-23 10:06:53 -05:00
Trevor Buckner
0b8889d0b8 Merge branch 'master' into compressBrews 2020-01-23 09:48:47 -05:00
Trevor Buckner
2efb24d692 Lint 2020-01-23 09:47:19 -05:00
Trevor Buckner
bc81e09686 Admin script to compress old brews
Also fully delete the text field after compressed version is saved instead of leaving just an empty field.
2020-01-23 09:38:50 -05:00
Eric Scheid
aeffec1763 minor grammar fixes in error messages 2020-01-21 10:07:35 -05:00
Eric Scheid
462a5608d2 Revert "list-style-type is a property of <li>, not <ul>"
This reverts commit 831bf5bc62.
2020-01-21 10:07:35 -05:00
Eric Scheid
09ae750eec list-style-type is a property of <li>, not <ul> 2020-01-21 10:07:35 -05:00
Trevor Buckner
e8dcb042f8 Typo 2020-01-21 10:05:22 -05:00
Trevor Buckner
ecd25ca49f Changed to encourage environment variable 2020-01-21 10:05:22 -05:00
Christian Brickhouse
3e8551bad6 Fix links to blobs in README.md 2020-01-21 10:05:22 -05:00
Christian Brickhouse
ef325e2617 Updating README.md with more detailed install instructions and reformatting. 2020-01-21 10:05:22 -05:00
Trevor Buckner
81c361bfb8 Make brand new files also save compressed 2020-01-10 16:03:04 -05:00
Trevor Buckner
37eb0d0889 Merge branch 'master' into compressBrews 2020-01-10 15:59:33 -05:00
Trevor Buckner
8adc04a565 Make brand new files also save compressed 2020-01-10 15:55:21 -05:00
Trevor Buckner
486841084f Make compression raw (no wrapper). Otherwise small files are actually larger after compression. 2020-01-10 09:07:28 -05:00
Trevor Buckner
399a6d82f6 lint 2020-01-10 09:07:28 -05:00
Trevor Buckner
becc6b8df0 Add zlib compression to the "text" field. Now, when a file is saved, the original text field is blanked out and the "compressed" binary version is saved instead. Viewing files in Edit page unzips and views them just fine.
Does not compress old files unless they are opened and resaved by someone. Have not tested on the "share" or "print" pages yet, but should work.
2020-01-10 09:07:28 -05:00
Trevor Buckner
1ff3f96f6c Make compression raw (no wrapper). Otherwise small files are actually larger after compression. 2020-01-10 08:59:39 -05:00
Trevor Buckner
b289cb1003 lint 2020-01-05 23:59:42 -05:00
Trevor Buckner
3ea3d273a5 Add zlib compression to the "text" field. Now, when a file is saved, the original text field is blanked out and the "compressed" binary version is saved instead. Viewing files in Edit page unzips and views them just fine.
Does not compress old files unless they are opened and resaved by someone. Have not tested on the "share" or "print" pages yet, but should work.
2020-01-05 23:48:50 -05:00
dependabot[bot]
a007c5f85f Bump lodash from 4.17.5 to 4.17.13
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.5 to 4.17.13.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.5...4.17.13)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-02 13:04:54 -04:00
Trevor Buckner
06d970e61a Notification about the Grey Shadow issue
First Reported 7/24/2019. Think it's a mobile Chrome issue.
2019-07-24 09:35:13 -04:00
Trevor Buckner
f28ed3d52e Try again... 2019-07-01 13:56:27 -04:00
Trevor Buckner
c5867dab91 Undo. Didn't work. 2019-07-01 13:20:24 -04:00
Trevor Buckner
df4bacf890 Allow cleaning function to work for a full minute 2019-07-01 13:16:26 -04:00
Trevor Buckner
6a57542216 Fix links on Home Page
Make github issues go to subreddit, update license link to Naturalcrit instead of stolksdorf
2019-05-02 08:38:48 -04:00
Trevor Buckner
72db7fedfb Remove "Report Issue" from share page
Too many users are reporting typos or brew issues thinking the "Report Issue" button is commenting on the document itself, instead of the website. Hoping this limits bug reports to just users who are actually editing documents.
2019-04-08 09:12:48 -04:00
Christian Brickhouse
892a5f9f1e Fixing links in contributing.md
Links to issue tags point to the project repo's Issues rather than a fork's.
2019-03-12 08:26:41 -04:00
Trevor Buckner
2839846ec0 Lint 2 2019-02-14 13:54:35 -05:00
Trevor Buckner
502ef6ad7c Lint 2019-02-14 13:54:35 -05:00
Trevor Buckner
5b8aa5bb19 Change the popup warning on the metadata editor 2019-02-14 13:54:35 -05:00
Trevor Buckner
a26e828f00 Change popup warning if you are the only editor
If you are the only editor, it will warn you that the document is about to be permanently deleted.
2019-02-14 13:54:35 -05:00
Trevor Buckner
f572e671cf Lint 2019-02-14 13:54:35 -05:00
Trevor Buckner
d18bd500b1 Fixing delete buttons
Delete should now first remove the current user as an editor, and only delete the file if he was the last editor. Currently, anyone who views your brew is added as an editor and if they delete it, the whole brew is deleted for everyone.
2019-02-14 13:54:35 -05:00
Trevor Buckner
14f721d209 Fix spacing on Notification popup for legibility 2019-02-07 12:24:26 -05:00
Trevor Buckner
1219f64cb3 Notification about Chrome PDF missing letters bug
Changed DISMISS KEY to current date to make popup appear again.
2019-02-07 12:19:38 -05:00
Trevor Buckner
b82aac4a5a Make Monster Blocks always generate "abilities"
Originally had a random chance to generate no abilities, which a user might not know how to insert with the proper formatting. Also added some new abilities to select randomly when generated. Also fixes #836 .
2019-02-04 11:26:37 -05:00
Trevor Buckner
b53b5ccf43 More version updates in the changelog 2019-01-29 17:40:39 -05:00
Bobby Wertman
9e7981f05c Fix typo in changelog
Fixes #765
2019-01-29 17:40:39 -05:00
Trevor Buckner
18a238786e Hide scrollbar in share page but KEEP SCROLLBAR IN USER PAGE 2019-01-22 13:42:46 -05:00
Trevor Buckner
507f8e0852 Revert "Revert "Merge branch 'master' of https://github.com/naturalcrit/homebrewery""
This reverts commit 0c70162a78.
2019-01-22 11:26:33 -05:00
Trevor Buckner
0c70162a78 Revert "Merge branch 'master' of https://github.com/naturalcrit/homebrewery"
This reverts commit 077511dfa7, reversing
changes made to facbc5f6dc.
2019-01-22 11:23:35 -05:00
Trevor Buckner
077511dfa7 Merge branch 'master' of https://github.com/naturalcrit/homebrewery 2019-01-22 11:13:53 -05:00
Trevor Buckner
69b25eb03a Fix duplicate scrollbar in "share" page 2019-01-22 10:54:14 -05:00
Trevor Buckner
a56a999920 More Lint 2019-01-19 20:11:16 -05:00
Trevor Buckner
c1d8796807 Lint 2019-01-19 20:11:16 -05:00
Trevor Buckner
71af97e489 Refactored the "Recent Items" navbar component. Greatly simplified the code to about 70% length and made it much easier to read. Results in the same thing.
Oh. And it works again. Recent brews should be showing up fine now.
2019-01-19 20:11:16 -05:00
Trevor Buckner
facbc5f6dc Merge branch 'master' of https://github.com/naturalcrit/homebrewery 2019-01-15 07:35:38 -05:00
Trevor Buckner
63a1ff454f Added a notification popup similar to the "YOU ARE NOT USING CHROME" message. Can be used to notify users of current known issues, updates, etc.
Currently just reminds users to back up documents with a link to the Reddit FAQ.
2019-01-11 16:31:08 -05:00
Trevor Buckner
c634192289 Added a notification popup similar to the "YOU ARE NOT USING CHROME" message. Can be used to notify users of current known issues, updates, etc.
Currently just reminds users to back up documents with a link to the Reddit FAQ.
2019-01-11 16:26:03 -05:00
Scott Tolksdorf
bf21c3d351 lint 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
52c0462a4f Finished the clean up brew ui 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
1184fe86a5 Removing the admin key, switching to full basic auth 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
7656e53606 Finished stats and brew lookup on admin panel 2018-12-06 18:19:43 -05:00
Scott Tolksdorf
4241052952 Updating readme 2018-12-05 18:14:05 -05:00
Scott Tolksdorf
21b83ead88 Added middleware to force all routes to be https 2018-12-05 18:14:05 -05:00
Scott Tolksdorf
448ea5cf5c Making the script tag stripper case insensitive 2018-12-03 09:55:49 +00:00
48 changed files with 2731 additions and 1957 deletions

3
.gitignore vendored
View File

@@ -7,4 +7,5 @@ storage
build/*
config/local.*
todo.md
todo.md
startDB.bat

View File

@@ -1,35 +1,48 @@
# The Homebrewery
The Homebrewery is a tool for making authentic looking [D&D content](http://dnd.wizards.com/products/tabletop-games/rpg-products/rpg_playershandbook) using only [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). Check it out [here](http://homebrewery.naturalcrit.com).
The Homebrewery is a tool for making authentic looking [D&D content](https://dnd.wizards.com/products/tabletop-games/rpg-products/rpg_playershandbook) using [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). It is distributed under the terms of the [MIT License](./license).
## Quick Start
The easiest way to get started using the Homebrewery is to use it [on our website](https://homebrewery.naturalcrit.com). The code is open source, so feel free to clone it, tinker with it. If you want to make changes to the code, you can run your own local version for testing by following the installation instructions below.
### issues, suggestions, bugs
If you run into any issues using The Homebrewery, please submit an issue [here](/issues).
### Installation
First, install two programs that the Homebrewery requires to run.
### local dev
The Homebrewery is open source, so feel free to clone it, tinker with it, or run your own local version.
#### pre-reqs
1. install [node](https://nodejs.org/en/)
1. install [mongodb](https://www.mongodb.com/)
#### getting started
1. clone it
Second, download a copy of the repository. If you have git you can do so with
```
git clone https://github.com/naturalcrit/homebrewery.git
```
Third, you will need to add the environment variable `NODE_ENV = local` to allow the project to run locally.
You can set this temporarily in your shell of choice:
* Windows Powershell: `$env:NODE_ENV="local"`
* Windows CMD: `set NODE_ENV=local`
* Linux / OSX: `export NODE_ENV=local`
Fourth, you will need to install the program and run it using the two commands:
1. `npm install`
1. `npm build`
1. `npm start`
#### 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 [here](https://github.com/stolksdorf/homebrewery/blob/master/phb.standalone.css).
You should now be able to go to [http://localhost:8000](http://localhost:8000) in your browser and use the Homebrewery offline.
### 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 are developing locally and would like to generate your own, follow the above steps and then run `npm run phb`.
### changelog
## Issues, Suggestions, and Bugs
If you run into any issues using The Homebrewery or have suggestions for improvement, please submit an issue [on GitHub](/issues). You can also get help for issues on the subreddit [r/homebrewery](https://www.reddit.com/r/homebrewery)
You can check out the changelog [here](https://github.com/stolksdorf/homebrewery/blob/master/changelog.md).
## Changelog
### license
You can check out the [changelog](./changelog.md).
This project is licensed under [MIT](./license). Which means you are free to use The Homebrewery in any way that you want, except for claiming that you made it yourself.
## License
This project is licensed under the [MIT license](./license). Which means you are free to use The Homebrewery in any way that you want, except for claiming that you made it yourself.
If you wish to sell or in some way gain profit for what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.

View File

@@ -1,8 +1,31 @@
# changelog
### Saturday, 22/04/217 - v2.7.4
- Give ability to hide the render warning notification
### Wednesday, 11/03/2020 - v2.8.2
- Fixed delete button removing everyone's copy for brews with multiple authors
- Compressed homebrew text in database
### Monday, 26/11/2018 - v2.8.1
- Fixed some SSL issues with images in the example page so they appear now
- Fixed duplicate scrollbars in Edit Page
- Fixed issue of being unable to change brew metadata
- Sanitized script tags-javascript typed into the editor was crashing brews
### Sunday, 08/04/2018 - v2.8.0
- Re-enabled box shadows for PDF output
- Added a "contributing guide" for the GitHub
- "Report Issue" navbar button now links to the subreddit
- Refactored background code
### Sunday, 04/06/2017 - v2.7.5
- Fixed the class feature snippet duplicating the entire brew
- Fixed headers in tables being duplicated
- Fixed border-image being scrambled on class tables and descriptive text boxes
- Fixed pages going out of sync in large brews, causing them to be rendered off-page
- Improved performance in the preview window when scrolling through large brews
- Text in the "view source" page now wraps
### Saturday, 22/04/2017 - v2.7.4
- Give ability to hide the render warning notification
### 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.

View File

@@ -1,38 +1,36 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx');
const BrewCleanup = require('./brewCleanup/brewCleanup.jsx');
const BrewLookup = require('./brewLookup/brewLookup.jsx');
const BrewCompress = require ('./brewCompress/brewCompress.jsx');
const Stats = require('./stats/stats.jsx');
const Admin = createClass({
getDefaultProps : function() {
return {
url : '',
admin_key : '',
homebrews : [],
};
return {};
},
render : function(){
return (
<div className='admin'>
<header>
<div className='container'>
<i className='fa fa-rocket' />
naturalcrit admin
</div>
</header>
return <div className='admin'>
<header>
<div className='container'>
<HomebrewAdmin homebrews={this.props.homebrews} admin_key={this.props.admin_key} />
<i className='fa fa-rocket' />
homebrewery admin
</div>
</header>
<div className='container'>
<Stats />
<hr />
<BrewLookup />
<hr />
<BrewCleanup />
<hr />
<BrewCompress />
</div>
);
</div>;
}
});

View File

@@ -36,4 +36,9 @@ body{
}
}
}
hr{
margin : 30px 0px;
}
}

View File

@@ -0,0 +1,74 @@
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const BrewCleanup = createClass({
displayName : 'BrewCleanup',
getDefaultProps(){
return {};
},
getInitialState() {
return {
count : 0,
pending : false,
primed : false,
err : null
};
},
prime(){
this.setState({ pending: true });
request.get('/admin/cleanup')
.then((res)=>this.setState({ count: res.body.count, primed: true }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ pending: false }));
},
cleanup(){
this.setState({ pending: true });
request.post('/admin/cleanup')
.then((res)=>this.setState({ count: res.body.count }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ pending: false, primed: false }));
},
renderPrimed(){
if(!this.state.primed) return;
if(!this.state.count){
return <div className='removeBox'>No Matching Brews found.</div>;
}
return <div className='removeBox'>
<button onClick={this.cleanup} className='remove'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: <span><i className='fa fa-times' /> Remove</span>
}
</button>
<span>Found {this.state.count} Brews that could be removed. </span>
</div>;
},
render(){
return <div className='BrewCleanup'>
<h2> Brew Cleanup </h2>
<p>Removes very short brews to tidy up the database</p>
<button onClick={this.prime} className='query'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: 'Query Brews'
}
</button>
{this.renderPrimed()}
{this.state.error
&& <div className='error'>{this.state.error.toString()}</div>
}
</div>;
}
});
module.exports = BrewCleanup;

View File

@@ -0,0 +1,10 @@
.BrewCleanup{
.removeBox{
margin-top: 20px;
button{
background-color: @red;
margin-right: 10px;
}
}
}

View File

@@ -0,0 +1,91 @@
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const BrewCompress = createClass({
displayName : 'BrewCompress',
getDefaultProps(){
return {};
},
getInitialState() {
return {
count : 0,
batchRange : 0,
pending : false,
primed : false,
err : null,
ids : null
};
},
prime(){
this.setState({ pending: true });
request.get('/admin/finduncompressed')
.then((res)=>this.setState({ count: res.body.count, primed: true, ids: res.body.ids }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ pending: false }));
},
cleanup(){
const brews = this.state.ids;
const compressBatches = ()=>{
if(brews.length == 0){
this.setState({ pending: false, primed: false });
return;
}
const batch = brews.splice(0, 1000); // Process brews in batches of 1000
this.setState({ batchRange: this.state.count - brews.length });
batch.forEach((id, idx)=>{
request.put(`/admin/compress/${id}`)
.catch((err)=>this.setState({ error: err }));
});
setTimeout(compressBatches, 10000); //Wait 10 seconds between batches
};
this.setState({ pending: true });
compressBatches();
},
renderPrimed(){
if(!this.state.primed) return;
if(!this.state.count){
return <div className='removeBox'>No Matching Brews found.</div>;
}
return <div className='removeBox'>
<button onClick={this.cleanup} className='remove'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: <span><i className='fa fa-compress' /> compress </span>
}
</button>
{this.state.pending
? <span>Compressing {this.state.batchRange} brews. </span>
: <span>Found {this.state.count} Brews that could be compressed. </span>
}
</div>;
},
render(){
return <div className='BrewCompress'>
<h2> Brew Compression </h2>
<p>Compresses the text in brews to binary</p>
<button onClick={this.prime} className='query'>
{this.state.pending
? <i className='fa fa-spin fa-spinner' />
: 'Query Brews'
}
</button>
{this.renderPrimed()}
{this.state.error
&& <div className='error'>{this.state.error.toString()}</div>
}
</div>;
}
});
module.exports = BrewCompress;

View File

@@ -0,0 +1,10 @@
.BrewCompress{
.removeBox{
margin-top: 20px;
button{
background-color: @red;
margin-right: 10px;
}
}
}

View File

@@ -0,0 +1,81 @@
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const Moment = require('moment');
const BrewLookup = createClass({
getDefaultProps() {
return {};
},
getInitialState() {
return {
query : '',
foundBrew : null,
searching : false,
error : null
};
},
handleChange(e){
this.setState({ query: e.target.value });
},
lookup(){
this.setState({ searching: true, error: null });
request.get(`/admin/lookup/${this.state.query}`)
.then((res)=>this.setState({ foundBrew: res.body }))
.catch((err)=>this.setState({ error: err }))
.finally(()=>this.setState({ searching: false }));
},
renderFoundBrew(){
const brew = this.state.foundBrew;
return <div className='foundBrew'>
<dl>
<dt>Title</dt>
<dd>{brew.title}</dd>
<dt>Authors</dt>
<dd>{brew.authors.join(', ')}</dd>
<dt>Edit Link</dt>
<dd><a href={`/edit/${brew.editId}`} target='_blank' rel='noopener noreferrer'>/edit/{brew.editId}</a></dd>
<dt>Share Link</dt>
<dd><a href={`/share/${brew.shareId}`} target='_blank' rel='noopener noreferrer'>/share/{brew.shareId}</a></dd>
<dt>Last Updated</dt>
<dd>{Moment(brew.updatedAt).fromNow()}</dd>
<dt>Num of Views</dt>
<dd>{brew.views}</dd>
</dl>
</div>;
},
render(){
return <div className='brewLookup'>
<h2>Brew Lookup</h2>
<input type='text' value={this.state.query} onChange={this.handleChange} placeholder='edit or share id' />
<button onClick={this.lookup}>
<i className={cx('fa', {
'fa-search' : !this.state.searching,
'fa-spin fa-spinner' : this.state.searching,
})} />
</button>
{this.state.error
&& <div className='error'>{this.state.error.toString()}</div>
}
{this.state.foundBrew
? this.renderFoundBrew()
: <div className='noBrew'>No brew found.</div>
}
</div>;
}
});
module.exports = BrewLookup;

View File

@@ -0,0 +1,30 @@
.brewLookup{
input{
height : 33px;
margin-bottom : 20px;
padding : 0px 10px;
font-family : monospace;
}
button{
vertical-align : middle;
height : 37px;
}
dl{
@maxItemWidth : 132px;
dt{
float : left;
clear : left;
width : @maxItemWidth;
text-align : right;
&::after {
content: " : ";
}
}
dd{
height : 1em;
margin-left : @maxItemWidth + 6px;
padding : 0 0 0.5em 0;
}
}
}

View File

@@ -1,68 +0,0 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const Moment = require('moment');
const BrewLookup = createClass({
getDefaultProps : function() {
return {
adminKey : '',
};
},
getInitialState : function() {
return {
query : '',
resultBrew : null,
searching : false
};
},
handleChange : function(e){
this.setState({
query : e.target.value
});
},
lookup : function(){
this.setState({ searching: true });
request.get(`/admin/lookup/${this.state.query}`)
.query({ admin_key: this.props.adminKey })
.end((err, res)=>{
this.setState({
searching : false,
resultBrew : (err ? null : res.body)
});
});
},
renderFoundBrew : function(){
if(this.state.searching) return <div className='searching'><i className='fa fa-spin fa-spinner' /></div>;
if(!this.state.resultBrew) return <div className='noBrew'>No brew found.</div>;
const brew = this.state.resultBrew;
return <div className='brewRow'>
<div>{brew.title}</div>
<div>{brew.authors.join(', ')}</div>
<div><a href={`/edit/${brew.editId}`} target='_blank' rel='noopener noreferrer'>/edit/{brew.editId}</a></div>
<div><a href={`/share/${brew.shareId}`} target='_blank' rel='noopener noreferrer'>/share/{brew.shareId}</a></div>
<div>{Moment(brew.updatedAt).fromNow()}</div>
<div>{brew.views}</div>
</div>;
},
render : function(){
return <div className='brewLookup'>
<h1>Brew Lookup</h1>
<input type='text' value={this.state.query} onChange={this.handleChange} placeholder='edit or share id...' />
<button onClick={this.lookup}><i className='fa fa-search'/></button>
{this.renderFoundBrew()}
</div>;
}
});
module.exports = BrewLookup;

View File

@@ -1,8 +0,0 @@
.brewLookup{
height : 200px;
input{
height : 33px;
padding : 0px 10px;
margin-bottom: 20px;
}
}

View File

@@ -1,66 +0,0 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const BrewSearch = createClass({
getDefaultProps : function() {
return {
admin_key : ''
};
},
getInitialState : function() {
return {
searchTerm : '',
brew : null,
};
},
search : function(){
request.get(`/homebrew/api/search?id=${this.state.searchTerm}`)
.query({
admin_key : this.props.admin_key,
})
.end((err, res)=>{
console.log(err, res, res.body.brews[0]);
this.setState({
brew : res.body.brews[0],
});
});
},
handleChange : function(e){
this.setState({
searchTerm : e.target.value
});
},
handleSearchClick : function(){
this.search();
},
renderBrew : function(){
if(!this.state.brew) return null;
return <div className='brew'>
<div>Edit id : {this.state.brew.editId}</div>
<div>Share id : {this.state.brew.shareId}</div>
</div>;
},
render : function(){
return <div className='search'>
<input type='text' value={this.state.searchTerm} onChange={this.handleChange} />
<button onClick={this.handleSearchClick}>Search</button>
{this.renderBrew()}
</div>;
},
});
module.exports = BrewSearch;

View File

@@ -1,170 +0,0 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const request = require('superagent');
const Moment = require('moment');
const BrewLookup = require('./brewLookup/brewLookup.jsx');
const HomebrewAdmin = createClass({
getDefaultProps : function() {
return {
admin_key : ''
};
},
getInitialState : function() {
return {
page : 0,
count : 20,
brewCache : {},
total : 0,
};
},
fetchBrews : function(page){
request.get('/api/search')
.query({
admin_key : this.props.admin_key,
count : this.state.count,
page : page
})
.end((err, res)=>{
if(err || !res.body || !res.body.brews) return;
const newCache = _.extend({}, this.state.brewCache);
newCache[page] = res.body.brews;
this.setState({
brewCache : newCache,
total : res.body.total,
count : res.body.count
});
});
},
componentDidMount : function() {
this.fetchBrews(this.state.page);
},
changePageTo : function(page){
if(!this.state.brewCache[page]){
this.fetchBrews(page);
}
this.setState({
page : page
});
},
clearInvalidBrews : function(){
request.get('/api/invalid')
.query({ admin_key: this.props.admin_key })
.end((err, res)=>{
if(!confirm(`This will remove ${res.body.count} brews. Are you sure?`)) return;
request.get('/api/invalid')
.query({ admin_key: this.props.admin_key, do_it: true })
.end((err, res)=>{
alert('Done!');
});
});
},
deleteBrew : function(brewId){
if(!confirm(`Are you sure you want to delete '${brewId}'?`)) return;
request.get(`/api/remove/${brewId}`)
.query({ admin_key: this.props.admin_key })
.end(function(err, res){
window.location.reload();
});
},
handlePageChange : function(dir){
this.changePageTo(this.state.page + dir);
},
renderPagnination : function(){
let outOf;
if(this.state.total){
outOf = `${this.state.page} / ${Math.round(this.state.total/this.state.count)}`;
}
return <div className='pagnination'>
<i className='fa fa-chevron-left' onClick={()=>this.handlePageChange(-1)}/>
{outOf}
<i className='fa fa-chevron-right' onClick={()=>this.handlePageChange(1)}/>
</div>;
},
renderBrews : function(){
const brews = this.state.brewCache[this.state.page] || _.times(this.state.count);
return _.map(brews, (brew)=>{
return <tr className={cx('brewRow', { 'isEmpty': brew.text == 'false' })} key={brew.shareId || brew}>
<td><a href={`/edit/${brew.editId}`} target='_blank' rel='noopener noreferrer'>{brew.editId}</a></td>
<td><a href={`/share/${brew.shareId}`} target='_blank' rel='noopener noreferrer'>{brew.shareId}</a></td>
<td>{Moment(brew.createdAt).fromNow()}</td>
<td>{Moment(brew.updatedAt).fromNow()}</td>
<td>{Moment(brew.lastViewed).fromNow()}</td>
<td>{brew.views}</td>
<td>
<div className='deleteButton' onClick={()=>this.deleteBrew(brew.editId)}>
<i className='fa fa-trash' />
</div>
</td>
</tr>;
});
},
renderBrewTable : function(){
return <div className='brewTable'>
<table>
<thead>
<tr>
<th>Edit Id</th>
<th>Share Id</th>
<th>Created At</th>
<th>Last Updated</th>
<th>Last Viewed</th>
<th>Views</th>
</tr>
</thead>
<tbody>
{this.renderBrews()}
</tbody>
</table>
</div>;
},
render : function(){
return <div className='homebrewAdmin'>
<BrewLookup adminKey={this.props.admin_key} />
{/*
<h2>
Homebrews - {this.state.total}
</h2>
{this.renderPagnination()}
{this.renderBrewTable()}
<button className='clearOldButton' onClick={this.clearInvalidBrews}>
Clear Old
</button>
<BrewSearch admin_key={this.props.admin_key} />
*/}
</div>;
}
});
module.exports = HomebrewAdmin;

View File

@@ -1,53 +0,0 @@
.homebrewAdmin{
margin-bottom: 80px;
.brewTable{
table{
th{
padding : 10px;
font-weight : 800;
}
tr:nth-child(even){
background-color : fade(@green, 10%);
}
tr.isEmpty{
background-color : fade(@red, 30%);
}
td{
min-width : 100px;
padding : 10px;
text-align : center;
&.preview{
position : relative;
&:hover{
.content{
display : block;
}
}
.content{
position : absolute;
display : none;
top : 100%;
left : 0px;
z-index : 1000;
max-height : 500px;
width : 300px;
padding : 30px;
background-color : white;
font-family : monospace;
text-align : left;
pointer-events : none;
}
}
}
}
}
.deleteButton{
cursor: pointer;
}
button.clearOldButton{
float : right;
}
}

View File

@@ -0,0 +1,45 @@
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
const request = require('superagent');
const Stats = createClass({
displayName : 'Stats',
getDefaultProps(){
return {};
},
getInitialState(){
return {
stats : {
totalBrews : 0
},
fetching : false
};
},
componentDidMount(){
this.fetchStats();
},
fetchStats(){
this.setState({ fetching: true });
request.get('/admin/stats')
.then((res)=>this.setState({ stats: res.body }))
.finally(()=>this.setState({ fetching: false }));
},
render(){
return <div className='Stats'>
<h2> Stats </h2>
<dl>
<dt>Total Brew Count</dt>
<dd>{this.state.stats.totalBrews}</dd>
</dl>
{this.state.fetching
&& <div className='pending'><i className='fa fa-spin fa-spinner' /></div>
}
</div>;
}
});
module.exports = Stats;

View File

@@ -0,0 +1,28 @@
.Stats{
position : relative;
.pending{
position : absolute;
top : 0px;
left : 0px;
height : 100%;
width : 100%;
background-color : rgba(238,238,238, 0.5);
}
dl{
@maxItemWidth : 132px;
dt{
float : left;
clear : left;
width : @maxItemWidth;
text-align : right;
&::after {
content: " : ";
}
}
dd{
margin : 0 0 0 @maxItemWidth + 10px;
padding : 0 0 0.5em 0;
}
}
}

View File

@@ -8,6 +8,7 @@ const ErrorBar = require('./errorBar/errorBar.jsx');
//TODO: move to the brew renderer
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
const PAGE_HEIGHT = 1056;
const PPR_THRESHOLD = 50;
@@ -132,7 +133,10 @@ const BrewRenderer = createClass({
style={{ height: this.state.height }}>
<ErrorBar errors={this.props.errors} />
<RenderWarnings />
<div className='popups'>
<RenderWarnings />
<NotificationPopup />
</div>
<div className='pages' ref='pages'>
{this.renderPages()}

View File

@@ -36,13 +36,13 @@ const ErrorBar = createClass({
const msg = [];
if(this.hasOpenError){
msg.push(<div>
An unmatched opening tag means there's an opened tag that isn't closed, you need to close a tag, like this {'</div>'}. Make sure to match types!
An unmatched opening tag means there's an opened tag that isn't closed. You need to close your tags, like this {'</div>'}. Make sure to match types!
</div>);
}
if(this.hasCloseError){
msg.push(<div>
An unmatched closing tag means you closed a tag without opening it. Either remove it, you check to where you think you opened it.
An unmatched closing tag means you closed a tag without opening it. Either remove it, or check to where you think you opened it.
</div>);
}

View File

@@ -0,0 +1,67 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames'); //Unused variable
const DISMISS_KEY = 'dismiss_notification7-24-19';
const NotificationPopup = createClass({
getInitialState : function() {
return {
notifications : {}
};
},
componentDidMount : function() {
this.checkNotifications();
window.addEventListener('resize', this.checkNotifications);
},
componentWillUnmount : function() {
window.removeEventListener('resize', this.checkNotifications);
},
notifications : {
psa : function(){
return <li key='psa'>
<em>Known bug: Grey Shadow Boxes </em> <br />
The shadows around certain brew elements such as notes and statblocks might appear as a solid grey box when generating a PDF. &nbsp;
<a target='_blank' href='https://old.reddit.com/r/homebrewery/comments/ch3v0d/psa_grey_boxesshadows_around_notes_stat_blocks_etc/'>
See this Reddit post
</a> for updates and possible workarounds.
</li>;
},
faq : function(){
return <li key='faq'>
<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! &nbsp;
<a target='_blank' href='https://www.reddit.com/r/homebrewery/comments/adh6lh/faqs_psas_announcements/'>
See the FAQ
</a> to learn how to avoid losing your work!
</li>;
},
},
checkNotifications : function(){
const hideDismiss = localStorage.getItem(DISMISS_KEY);
if(hideDismiss) return this.setState({ notifications: {} });
this.setState({
notifications : _.mapValues(this.notifications, (fn)=>{ return fn(); }) //Convert notification functions into their return text value
});
},
dismiss : function(){
localStorage.setItem(DISMISS_KEY, true);
this.checkNotifications();
},
render : function(){
if(_.isEmpty(this.state.notifications)) return null;
return <div className='notificationPopup'>
<i className='fa fa-times dismiss' onClick={this.dismiss}/>
<i className='fa fa-info-circle info' />
<h3>Notice</h3>
<small>This website is always improving and we are still adding new features and squashing bugs. Keep the following in mind:</small>
<ul>{_.values(this.state.notifications)}</ul>
</div>;
}
});
module.exports = NotificationPopup;

View File

@@ -0,0 +1,62 @@
.popups{
position : fixed;
top : @navbarHeight;
right : 15px;
z-index : 10001;
width : 350px;
}
.notificationPopup{
position : relative;
float : right;
display : inline-block;
width : 350px;
padding : 20px;
padding-bottom : 10px;
padding-left : 85px;
background-color : @blue;
color : white;
a{
color : @steel;
font-weight : 800;
}
i.info{
position : absolute;
top : 24px;
left : 24px;
opacity : 0.8;
font-size : 2.5em;
}
i.dismiss{
position : absolute;
top : 10px;
right : 10px;
cursor : pointer;
opacity : 0.6;
&:hover{
opacity : 1;
}
}
small{
opacity : 0.7;
font-size : 0.6em;
}
h3{
font-size : 1.1em;
font-weight : 800;
}
ul{
margin-top : 15px;
font-size : 0.8em;
list-style-position : outside;
list-style-type : disc;
li{
font-size : 0.8em;
line-height : 1.4em;
margin-top : 1.4em;
em{
font-weight : 800;
}
}
}
}

View File

@@ -42,8 +42,13 @@ const MetadataEditor = createClass({
},
handleDelete : function(){
if(!confirm('are you sure you want to delete this brew?')) return;
if(!confirm('are you REALLY sure? You will not be able to recover it')) return;
if(this.props.metadata.authors.length <= 1){
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return;
} else {
if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the 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}`)
.send()

View File

@@ -72,7 +72,7 @@ module.exports = {
let slots = 2;
return `<div class='classTable wide'>\n##### The ${classname}\n` +
`| Level | Proficiency Bonus | Features | Cantrips Known | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |\n`+
`|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n${
`|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n${
_.map(levels, function(levelName, level){
const res = [
levelName,
@@ -111,4 +111,4 @@ module.exports = {
return `| ${res} |`;
}).join('\n')}\n</div>\n\n`;
}
};
};

View File

@@ -96,7 +96,11 @@ const getStats = function(){
const genAbilities = function(){
return _.sample([
'> ***Pack Tactics.*** These guys work together. Like super well, you don\'t even know.',
'> ***False Appearance. *** While the armor reamin motionless, it is indistinguishable from a normal suit of armor.',
'> ***Fowl Appearance.*** While the creature remains motionless, it is indistinguishable from a normal chicken.',
'> ***Onion Stench.*** Any creatures within 5 feet of this thing develops an irrational craving for onion rings.',
'> ***Enormous Nose.*** This creature gains advantage on any check involving putting things in its nose.',
'> ***Sassiness.*** When questioned, this creature will talk back instead of answering.',
'> ***Big Jerk.*** Thinks he is just *waaaay* better than you.',
]);
};
@@ -184,7 +188,7 @@ module.exports = {
`> - **Languages** ${genList(['Common', 'Pottymouth', 'Gibberish', 'Latin', 'Jive'], 2)}`,
`> - **Challenge** ${_.random(0, 15)} (${_.random(10, 10000)} XP)`,
'> ___',
_.times(_.random(0, 2), function(){
_.times(_.random(2, 3), function(){
return genAbilities();
}).join('\n>\n'),
'> ### Actions',

View File

@@ -8,9 +8,9 @@
background-color : @steel;
flex-direction : column;
.content{
position : relative;
height : calc(~"100% - 29px"); //Navbar height
flex : auto;
position : relative;
height : calc(~"100% - 29px"); //Navbar height
flex : auto;
}
}
}
}

View File

@@ -1,51 +1,72 @@
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const cx = require('classnames');
const Moment = require('moment');
const Nav = require('naturalcrit/nav/nav.jsx');
const VIEW_KEY = 'homebrewery-recently-viewed';
const EDIT_KEY = 'homebrewery-recently-edited';
const VIEW_KEY = 'homebrewery-recently-viewed';
const RecentItems = createClass({
const BaseItem = createClass({
getDefaultProps : function() {
return {
storageKey : '',
text : '',
currentBrew : {
title : '',
id : '',
url : ''
}
storageKey : '',
showEdit : false,
showView : false
};
},
getInitialState : function() {
return {
showDropdown : false,
brews : []
edit : [],
view : []
};
},
componentDidMount : function() {
let brews = JSON.parse(localStorage.getItem(this.props.storageKey) || '[]');
brews = _.filter(brews, (brew)=>{
return brew.id !== this.props.currentBrew.id;
});
if(this.props.currentBrew.id){
brews.unshift({
id : this.props.currentBrew.id,
url : this.props.currentBrew.url,
title : this.props.currentBrew.title,
//== Load recent items list ==//
let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]');
let viewed = JSON.parse(localStorage.getItem(VIEW_KEY) || '[]');
//== Add current brew to appropriate recent items list (depending on storageKey) ==//
if(this.props.storageKey == 'edit'){
edited = _.filter(edited, (brew)=>{
return brew.id !== this.props.brew.editId;
});
edited.unshift({
id : this.props.brew.editId,
title : this.props.brew.title,
url : `/edit/${this.props.brew.editId}`,
ts : Date.now()
});
}
brews = _.slice(brews, 0, 8);
localStorage.setItem(this.props.storageKey, JSON.stringify(brews));
if(this.props.storageKey == 'view'){
viewed = _.filter(viewed, (brew)=>{
return brew.id !== this.props.brew.shareId;
});
viewed.unshift({
id : this.props.brew.shareId,
title : this.props.brew.title,
url : `/share/${this.props.brew.shareId}`,
ts : Date.now()
});
}
//== Store the updated lists (up to 8 items each) ==//
edited = _.slice(edited, 0, 8);
viewed = _.slice(viewed, 0, 8);
localStorage.setItem(EDIT_KEY, JSON.stringify(edited));
localStorage.setItem(VIEW_KEY, JSON.stringify(viewed));
this.setState({
brews : brews
edit : edited,
view : viewed
});
},
@@ -58,14 +79,25 @@ const BaseItem = createClass({
renderDropdown : function(){
if(!this.state.showDropdown) return null;
const items = _.map(this.state.brews, (brew)=>{
return <a href={brew.url} className='item' key={brew.id} target='_blank' rel='noopener noreferrer'>
<span className='title'>{brew.title}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span>
</a>;
});
const makeItems = (brews)=>{
return _.map(brews, (brew)=>{
return <a href={brew.url} className='item' key={brew.id} target='_blank' rel='noopener noreferrer'>
<span className='title'>{brew.title || '[ no title ]'}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span>
</a>;
});
};
return <div className='dropdown'>{items}</div>;
return <div className='dropdown'>
{(this.props.showEdit && this.props.showView) ?
<h4>edited</h4> : null }
{this.props.showEdit ?
makeItems(this.state.edit) : null }
{(this.props.showEdit && this.props.showView) ?
<h4>viewed</h4> : null }
{this.props.showView ?
makeItems(this.state.view) : null }
</div>;
},
render : function(){
@@ -75,126 +107,37 @@ const BaseItem = createClass({
{this.props.text}
{this.renderDropdown()}
</Nav.item>;
},
}
});
module.exports = {
viewed : createClass({
getDefaultProps : function() {
return {
brew : {
title : '',
shareId : ''
}
};
},
render : function(){
return <BaseItem text='recently viewed' storageKey={VIEW_KEY}
currentBrew={{
id : this.props.brew.shareId,
title : this.props.brew.title,
url : `/share/${this.props.brew.shareId}`
}}
/>;
},
}),
edited : createClass({
getDefaultProps : function() {
return {
brew : {
title : '',
editId : ''
}
};
},
render : function(){
return <BaseItem text='recently edited' storageKey={EDIT_KEY}
currentBrew={{
id : this.props.brew.editId,
title : this.props.brew.title,
url : `/edit/${this.props.brew.editId}`
}}
/>;
},
}),
edited : (props)=>{
return <RecentItems
brew={props.brew}
storageKey={props.storageKey}
text='recently edited'
showEdit={true}
/>;
},
both : createClass({
getDefaultProps : function() {
return {
errorId : null
};
},
viewed : (props)=>{
return <RecentItems
brew={props.brew}
storageKey={props.storageKey}
text='recently viewed'
showView={true}
/>;
},
getInitialState : function() {
return {
showDropdown : false,
edit : [],
view : []
};
},
componentDidMount : function() {
let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]');
let viewed = JSON.parse(localStorage.getItem(VIEW_KEY) || '[]');
if(this.props.errorId){
edited = _.filter(edited, (edit)=>{
return edit.id !== this.props.errorId;
});
viewed = _.filter(viewed, (view)=>{
return view.id !== this.props.errorId;
});
localStorage.setItem(EDIT_KEY, JSON.stringify(edited));
localStorage.setItem(VIEW_KEY, JSON.stringify(viewed));
}
this.setState({
edit : edited,
view : viewed
});
},
handleDropdown : function(show){
this.setState({
showDropdown : show
});
},
renderDropdown : function(){
if(!this.state.showDropdown) return null;
const makeItems = (brews)=>{
return _.map(brews, (brew)=>{
return <a href={brew.url} className='item' key={brew.id} target='_blank' rel='noopener noreferrer'>
<span className='title'>{brew.title}</span>
<span className='time'>{Moment(brew.ts).fromNow()}</span>
</a>;
});
};
return <div className='dropdown'>
<h4>edited</h4>
{makeItems(this.state.edit)}
<h4>viewed</h4>
{makeItems(this.state.view)}
</div>;
},
render : function(){
return <Nav.item icon='fa-clock-o' color='grey' className='recent'
onMouseEnter={()=>this.handleDropdown(true)}
onMouseLeave={()=>this.handleDropdown(false)}>
Recent brews
{this.renderDropdown()}
</Nav.item>;
}
})
both : (props)=>{
return <RecentItems
brew={props.brew}
storageKey={props.storageKey}
text='recent brews'
showEdit={true}
showView={true}
/>;
}
};

View File

@@ -10,7 +10,7 @@ const Navbar = require('../../navbar/navbar.jsx');
const ReportIssue = require('../../navbar/issue.navitem.jsx');
const PrintLink = require('../../navbar/print.navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
//const RecentlyEdited = require('../../navbar/recent.navitem.jsx').edited;
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
@@ -186,14 +186,15 @@ const EditPage = createClass({
<Nav.section>
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
{this.renderSaveButton()}
{/*<RecentlyEdited brew={this.props.brew} />*/}
<ReportIssue />
<Nav.item newTab={true} href={`/share/${this.props.brew.shareId}`} color='teal' icon='fa-share-alt'>
Share
</Nav.item>
<PrintLink shareId={this.props.brew.shareId} />
<RecentNavItem brew={this.props.brew} storageKey='edit' />
<Account />
</Nav.section>
</Navbar>;

View File

@@ -7,7 +7,7 @@ const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -33,7 +33,7 @@ const ErrorPage = createClass({
<Nav.section>
<PatreonNavItem />
<IssueNavItem />
<RecentNavItem.both errorId={this.props.errorId} />
<RecentNavItem />
</Nav.section>
</Navbar>

View File

@@ -8,7 +8,7 @@ const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PatreonNavItem = require('../../navbar/patreon.navitem.jsx');
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const AccountNavItem = require('../../navbar/account.navitem.jsx');
@@ -59,7 +59,7 @@ const HomePage = createClass({
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
Changelog
</Nav.item>
<RecentNavItem.both />
<RecentNavItem />
<AccountNavItem />
{/*}
<Nav.item href='/new' color='green' icon='fa-external-link'>

View File

@@ -43,10 +43,10 @@ With the next major release of Homebrewery, v3.0.0, this tool *will no longer su
What's new in the latest update? Check out the full changelog [here](/changelog)
### Bugs, Issues, Suggestions?
Have an idea of how to make The Homebrewery better? Or did you find something that wasn't quite right? Head [here](https://github.com/stolksdorf/homebrewery/issues/new) and let me know!.
Have an idea of how to make The Homebrewery better? Or did you find something that wasn't quite right? Head [here](https://www.reddit.com/r/homebrewery/submit?selftext=true&title=%5BIssue%5D%20Describe%20Your%20Issue%20Here) and let me know!.
### Legal Junk
The Homebrewery is licensed using the [MIT License](https://github.com/stolksdorf/homebrewery/blob/master/license). Which means you are free to use The Homebrewery is any way that you want, except for claiming that you made it yourself.
The Homebrewery is licensed using the [MIT License](https://github.com/naturalcrit/homebrewery/blob/master/license). Which means you are free to use The Homebrewery is any way that you want, except for claiming that you made it yourself.
If you wish to sell or in some way gain profit for what's created on this site, it's your responsibility to ensure you have the proper licenses/rights for any images or resources used.

View File

@@ -9,6 +9,7 @@ const Markdown = require('naturalcrit/markdown.js');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const IssueNavItem = require('../../navbar/issue.navitem.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
@@ -135,6 +136,7 @@ const NewPage = createClass({
{this.renderSaveButton()}
{this.renderLocalPrintButton()}
<IssueNavItem />
<RecentNavItem />
<AccountNavItem />
</Nav.section>
</Navbar>;

View File

@@ -6,8 +6,7 @@ const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const PrintLink = require('../../navbar/print.navitem.jsx');
const ReportIssue = require('../../navbar/issue.navitem.jsx');
//const RecentlyViewed = require('../../navbar/recent.navitem.jsx').viewed;
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const Account = require('../../navbar/account.navitem.jsx');
@@ -52,12 +51,11 @@ const SharePage = createClass({
</Nav.section>
<Nav.section>
<ReportIssue />
{/*<RecentlyViewed brew={this.props.brew} />*/}
<PrintLink shareId={this.props.brew.shareId} />
<Nav.item href={`/source/${this.props.brew.shareId}`} color='teal' icon='fa-code'>
source
</Nav.item>
<RecentNavItem brew={this.props.brew} storageKey='view' />
<Account />
</Nav.section>
</Navbar>

View File

@@ -1,3 +1,5 @@
.sharePage{
.content{
overflow-y : hidden;
}
}

View File

@@ -18,8 +18,13 @@ const BrewItem = createClass({
},
deleteBrew : function(){
if(!confirm('are you sure you want to delete this brew?')) return;
if(!confirm('are you REALLY sure? You will not be able to recover it')) return;
if(this.props.brew.authors.length <= 1){
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return;
} else {
if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the 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}`)
.send()

View File

@@ -6,7 +6,7 @@ const cx = require('classnames');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const Account = require('../../navbar/account.navitem.jsx');
const BrewItem = require('./brewItem/brewItem.jsx');
@@ -42,31 +42,27 @@ const UserPage = createClass({
});
},
renderPrivateBrews : function(privateBrews){
if(!privateBrews || !privateBrews.length) return;
return [
<h1>{this.props.username}'s unpublished brews</h1>,
this.renderBrews(privateBrews)
];
},
render : function(){
const brews = this.getSortedBrews();
return <div className='userPage page'>
<Navbar>
<Nav.section>
<RecentNavItem.both />
<RecentNavItem />
<Account />
</Nav.section>
</Navbar>
<div className='content'>
<div className='phb'>
<h1>{this.props.username}'s brews</h1>
{this.renderBrews(brews.published)}
{this.renderPrivateBrews(brews.private)}
<div>
<h1>{this.props.username}'s brews</h1>
{this.renderBrews(brews.published)}
</div>
<div>
<h1>{this.props.username}'s unpublished brews</h1>
{this.renderBrews(brews.private)}
</div>
</div>
</div>
</div>;

View File

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

View File

@@ -16,10 +16,10 @@ Some issues are created with missing information, not reproducible, or plain inv
You can use issue labels to discover issues you could help out with:
* [`blocked` issues](https://github.com/stolksdorf/homebrewery/labels/blocked) need help getting unstuck
* [`bug` issues](https://github.com/stolksdorf/homebrewery/labels/bug) are known bugs we'd like to fix
* [`feature` issues](https://github.com/stolksdorf/homebrewery/labels/feature) are features we're open to including
* [`help wanted`](https://github.com/stolksdorf/homebrewery/labels/help%20wanted) labels are especially useful.
* [`blocked` issues](https://github.com/naturalcrit/homebrewery/labels/blocked) need help getting unstuck
* [`bug` issues](https://github.com/naturalcrit/homebrewery/labels/bug) are known bugs we'd like to fix
* [`feature` issues](https://github.com/naturalcrit/homebrewery/labels/feature) are features we're open to including
* [`help wanted`](https://github.com/naturalcrit/homebrewery/labels/help%20wanted) labels are especially useful.
If you're updating dependencies, please make sure you use npm@5.6.0 and commit the updated `package-lock.json` file.

3111
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
"version": "2.8.1",
"version": "2.8.2",
"repository": {
"type": "git",
"url": "git://github.com/stolksdorf/homebrewery.git"
"url": "git://github.com/naturalcrit/homebrewery.git"
},
"scripts": {
"dev": "node scripts/dev.js",
@@ -36,31 +36,30 @@
]
},
"dependencies": {
"babel-preset-env": "^1.1.8",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"basic-auth": "^2.0.0",
"body-parser": "^1.14.2",
"classnames": "^2.2.0",
"codemirror": "^5.22.0",
"cookie-parser": "^1.4.3",
"body-parser": "^1.19.0",
"classnames": "^2.2.6",
"codemirror": "^5.52.0",
"cookie-parser": "^1.4.4",
"create-react-class": "^15.6.3",
"express": "^4.13.3",
"jwt-simple": "^0.5.1",
"lodash": "^4.11.2",
"marked": "^0.3.5",
"moment": "^2.11.0",
"mongoose": "^5.0.13",
"express": "^4.17.1",
"jwt-simple": "^0.5.6",
"lodash": "^4.17.15",
"marked": "^0.3.19",
"moment": "^2.24.0",
"mongoose": "^5.9.2",
"nconf": "^0.10.0",
"pico-router": "^2.1.0",
"react": "^16.3.1",
"react-dom": "^16.3.1",
"shortid": "^2.2.4",
"superagent": "^3.8.2",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"shortid": "^2.2.15",
"superagent": "^5.2.2",
"vitreum": "^4.10.1"
},
"devDependencies": {
"eslint": "^4.19.1",
"eslint-plugin-react": "^7.7.0",
"pico-check": "^1.0.3"
"eslint": "^6.8.0",
"eslint-plugin-react": "^7.19.0",
"pico-check": "^1.3.2"
}
}

View File

@@ -6,6 +6,7 @@ const app = express();
app.use(express.static(`${__dirname}/build`));
app.use(require('body-parser').json({ limit: '25mb' }));
app.use(require('cookie-parser')());
app.use(require('./server/forcessl.mw.js'));
const config = require('nconf')
.argv()
@@ -15,7 +16,8 @@ const config = require('nconf')
//DB
const mongoose = require('mongoose');
mongoose.connect(config.get('mongodb_uri') || config.get('mongolab_uri') || 'mongodb://localhost/naturalcrit');
mongoose.connect(config.get('mongodb_uri') || config.get('mongolab_uri') || 'mongodb://localhost/naturalcrit',
{ retryWrites: false, useNewUrlParser: true });
mongoose.connection.on('error', ()=>{
console.log('Error : Could not connect to a Mongo Database.');
console.log(' If you are running locally, make sure mongodb.exe is running.');
@@ -23,7 +25,7 @@ mongoose.connection.on('error', ()=>{
});
//Account MIddleware
//Account Middleware
app.use((req, res, next)=>{
if(req.cookies && req.cookies.nc_session){
try {

View File

@@ -1,51 +1,61 @@
const _ = require('lodash');
const auth = require('basic-auth');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
const Moment = require('moment');
const render = require('vitreum/steps/render');
const templateFn = require('../client/template.js');
const zlib = require('zlib');
process.env.ADMIN_USER = process.env.ADMIN_USER || 'admin';
process.env.ADMIN_PASS = process.env.ADMIN_PASS || 'password3';
const mw = {
adminOnly : (req, res, next)=>{
if(req.query && req.query.admin_key == process.env.ADMIN_KEY) return next();
if(!req.get('authorization')){
return res
.set('WWW-Authenticate', 'Basic realm="Authorization Required"')
.status(401)
.send('Authorization Required');
}
const [username, password] = new Buffer(req.get('authorization').split(' ').pop(), 'base64')
.toString('ascii')
.split(':');
if(process.env.ADMIN_USER === username && process.env.ADMIN_PASS === password){
return next();
}
return res.status(401).send('Access denied');
}
};
process.env.ADMIN_USER = process.env.ADMIN_USER || 'admin';
process.env.ADMIN_PASS = process.env.ADMIN_PASS || 'password';
process.env.ADMIN_KEY = process.env.ADMIN_KEY || 'admin_key';
//Removes all empty brews that are older than 3 days and that are shorter than a tweet
router.get('/api/invalid', mw.adminOnly, (req, res)=>{
const invalidBrewQuery = HomebrewModel.find({
'$where' : 'this.text.length < 140',
createdAt : {
$lt : Moment().subtract(3, 'days').toDate()
}
});
if(req.query.do_it){
invalidBrewQuery.remove().exec((err, objs)=>{
refreshCount();
return res.send(200);
});
} else {
invalidBrewQuery.exec((err, objs)=>{
if(err) console.log(err);
return res.json({
count : objs.length
});
});
/* Search for brews that are older than 3 days and that are shorter than a tweet */
const junkBrewQuery = HomebrewModel.find({
'$where' : 'this.text.length < 140',
createdAt : {
$lt : Moment().subtract(30, 'days').toDate()
}
}).limit(100).maxTime(60000);
/* Search for brews that aren't compressed (missing the compressed text field) */
const uncompressedBrewQuery = HomebrewModel.find({
'textBin' : null
}).lean().limit(10000).select('_id');
router.get('/admin/cleanup', mw.adminOnly, (req, res)=>{
junkBrewQuery.exec((err, objs)=>{
if(err) return res.status(500).send(err);
return res.json({ count: objs.length });
});
});
/* Removes all empty brews that are older than 3 days and that are shorter than a tweet */
router.post('/admin/cleanup', mw.adminOnly, (req, res)=>{
junkBrewQuery.remove().exec((err, objs)=>{
if(err) return res.status(500).send(err);
return res.json({ count: objs.length });
});
});
/* Searches for matching edit or share id, also attempts to partial match */
router.get('/admin/lookup/:id', mw.adminOnly, (req, res, next)=>{
//search for mathcing edit id
//search for matching share id
// search for partial match
HomebrewModel.findOne({ $or : [
{ editId: { '$regex': req.params.id, '$options': 'i' } },
{ shareId: { '$regex': req.params.id, '$options': 'i' } },
@@ -54,32 +64,47 @@ router.get('/admin/lookup/:id', mw.adminOnly, (req, res, next)=>{
});
});
/* Find 50 brews that aren't compressed yet */
router.get('/admin/finduncompressed', mw.adminOnly, (req, res)=>{
uncompressedBrewQuery.exec((err, objs)=>{
if(err) return res.status(500).send(err);
objs = objs.map((obj)=>{return obj._id;});
return res.json({ count: objs.length, ids: objs });
});
});
/* Compresses the "text" field of a brew to binary */
router.put('/admin/compress/:id', (req, res)=>{
HomebrewModel.get({ _id: req.params.id })
.then((brew)=>{
brew.textBin = zlib.deflateRawSync(brew.text); // Compress brew text to binary before saving
brew.text = undefined; // Delete the non-binary text field since it's not needed anymore
//Admin route
const render = require('vitreum/steps/render');
const templateFn = require('../client/template.js');
router.get('/admin', function(req, res){
const credentials = auth(req);
if(!credentials || credentials.name !== process.env.ADMIN_USER || credentials.pass !== process.env.ADMIN_PASS) {
res.setHeader('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send('Access denied');
}
render('admin', templateFn, {
url : req.originalUrl,
admin_key : process.env.ADMIN_KEY,
})
.then((page)=>{
return res.send(page);
brew.save((err, obj)=>{
if(err) throw err;
return res.status(200).send(obj);
});
})
.catch((err)=>{
console.log(err);
return res.sendStatus(500);
return res.status(500).send('Error while saving');
});
});
router.get('/admin/stats', mw.adminOnly, (req, res)=>{
HomebrewModel.count({}, (err, count)=>{
return res.json({
totalBrews : count
});
});
});
router.get('/admin', mw.adminOnly, (req, res)=>{
render('admin', templateFn, {
url : req.originalUrl
})
.then((page)=>res.send(page))
.catch((err)=>res.sendStatus(500));
});
module.exports = router;
module.exports = router;

7
server/forcessl.mw.js Normal file
View File

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

View File

@@ -1,6 +1,7 @@
const _ = require('lodash');
const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router();
const zlib = require('zlib');
// const getTopBrews = (cb)=>{
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
@@ -31,9 +32,14 @@ router.post('/api', (req, res)=>{
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
newHomebrew.text = undefined; // Delete the non-binary text field since it's not needed anymore
newHomebrew.save((err, obj)=>{
if(err){
console.error(err, err.toString(), err.stack);
@@ -47,7 +53,10 @@ router.put('/api/update/:id', (req, res)=>{
HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{
brew = _.merge(brew, req.body);
brew.textBin = zlib.deflateRawSync(req.body.text); // Compress brew text to binary before saving
brew.text = undefined; // Delete the non-binary text field since it's not needed anymore
brew.updatedAt = new Date();
if(req.account) brew.authors = _.uniq(_.concat(brew.authors, req.account.username));
brew.markModified('authors');
@@ -67,11 +76,26 @@ router.put('/api/update/:id', (req, res)=>{
router.get('/api/remove/:id', (req, res)=>{
HomebrewModel.find({ editId: req.params.id }, (err, objs)=>{
if(!objs.length || err) return res.status(404).send('Can not find homebrew with that id');
const resEntry = objs[0];
resEntry.remove((err)=>{
if(err) return res.status(500).send('Error while removing');
return res.status(200).send();
});
const brew = objs[0];
// Remove current user as author
if(req.account){
brew.authors = _.pull(brew.authors, req.account.username);
brew.markModified('authors');
}
// Delete brew if there are no authors left
if(!brew.authors.length)
brew.remove((err)=>{
if(err) return res.status(500).send('Error while removing');
return res.status(200).send();
});
// Otherwise, save the brew with updated author list
else
brew.save((err, savedBrew)=>{
if(err) throw err;
return res.status(200).send(savedBrew);
});
});
});
@@ -127,4 +151,4 @@ module.exports = function(app){
return app;
}
*/
*/

View File

@@ -1,12 +1,14 @@
const mongoose = require('mongoose');
const shortid = require('shortid');
const _ = require('lodash');
const zlib = require('zlib');
const HomebrewSchema = mongoose.Schema({
shareId : { type: String, default: shortid.generate, index: { unique: true } },
editId : { type: String, default: shortid.generate, index: { unique: true } },
title : { type: String, default: '' },
text : { type: String, default: '' },
textBin : { type: Buffer },
description : { type: String, default: '' },
tags : { type: String, default: '' },
@@ -51,6 +53,10 @@ HomebrewSchema.statics.get = function(query){
return new Promise((resolve, reject)=>{
Homebrew.find(query, (err, brews)=>{
if(err || !brews.length) return reject('Can not find brew');
if(!_.isNil(brews[0].textBin)) { // Uncompress zipped text field
unzipped = zlib.inflateRawSync(brews[0].textBin);
brews[0].text = unzipped.toString();
}
return resolve(brews[0]);
});
});

View File

@@ -25,7 +25,7 @@ const RenderWarnings = createClass({
if(!isChrome){
return <li key='chrome'>
<em>Built for Chrome </em> <br />
Other browsers do not support
Other browsers do not support &nbsp;
<a target='_blank' href='https://developer.mozilla.org/en-US/docs/Web/CSS/column-span#Browser_compatibility'>
key features
</a> this site uses.

View File

@@ -1,16 +1,17 @@
.renderWarnings{
position : fixed;
position : relative;
float : right;
display : inline-block;
top : @navbarHeight;
right : 15px;
z-index : 10001;
width : 350px;
padding : 20px;
padding-bottom : 10px;
padding-left : 85px;
margin-bottom : 10px;
background-color : @yellow;
color : white;
a{
font-weight : 800;
}
i.ohno{
position : absolute;
top : 24px;

View File

@@ -15,8 +15,8 @@ renderer.html = function (html) {
const sanatizeScriptTags = (content)=>{
return content
.replace(/<script/g, '&lt;script')
.replace(/<\/script>/g, '&lt;/script&gt;');
.replace(/<script/ig, '&lt;script')
.replace(/<\/script>/ig, '&lt;/script&gt;');
};
const tagTypes = ['div', 'span', 'a'];