diff --git a/changelog.md b/changelog.md index 9d1ddf32d..1f7815d8d 100644 --- a/changelog.md +++ b/changelog.md @@ -81,9 +81,85 @@ pre { } ``` + ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Saturday 10/12/2024 - v3.16.0 + +{{taskList +##### 5e-Cleric + +* [x] Added a new API endpoint `/metadata/:shareId` to fetch metadata about individual brews + +Fixes issue [#2638](https://github.com/naturalcrit/homebrewery/issues/2638) + +* [x] Added A3, A5, and Card page size snippets under {{openSans **:fas_paintbrush: STYLE TAB :fas_arrow_right: :fas_print: PRINT**}} + +* [x] Adjust navbar styling for very long titles + +Fixes issue [#2071](https://github.com/naturalcrit/homebrewery/issues/2071) + +* [x] Added some sorting options to the {{openSans **VAULT** {{fas,fa-dungeon}}}} page + +* [x] Fix `language` property not working in share page + +Fixes issue [#3776](https://github.com/naturalcrit/homebrewery/issues/3776) + +##### abquintic + +* [x] New {{openSans **:fas_pencil: TEXT EDITOR :fas_arrow_right: :fas_bookmark: PAGE NUMBER :fas_arrow_right:**}} +{{openSans **:fas_xmark: SKIP PAGE NUMBER**}} and {{openSans **:fas_arrow_rotate_left: RESTART PAGE NUMBER**}} snippets for more control over automatic page numbering. + +Fixes issue [#513](https://github.com/naturalcrit/homebrewery/issues/513) + +* [x] New Table of Contents control options via {{openSans **:fas_pencil: TEXT EDITOR :fas_arrow_right: :fas_book: TABLE OF CONTENTS**}} submenus. By default, H1-H3 is included in the ToC generation, but the new options allow marking `{{blocks}}` to include or exclude specific or ranges of contained headers. Also, a global option to increase the default range of H1-H3 to H1-H4/5/6. After applying these markers, you must regenerate the Table of Contents to see the changes. + +* [x] Added a ":fas_lock: SYNC VIEWS" button onto the divider bar. When locked, scrolling on either panel will sync the other panel to the same page. + +Fixes issue [#241](https://github.com/naturalcrit/homebrewery/issues/241) + +##### Gazook89 + +* [x] Added a :fas_glasses: HIDE button to the page navigation bar + +##### G-Ambatte + +* [x] Automatic local backups of your files, in case of accidental data loss. Stores up to 5 snapshots of each brew edited in your browser, incrementing from a few minutes old to a maximum of several days. Restore a backup by clicking an entry in the new {{openSans **:fas_clock_rotate_left: HISTORY**}} button in the snippet bar. + +Fixes issue [#3070](https://github.com/naturalcrit/homebrewery/issues/3070) + +* [x] Fix issue with legacy brews breaking on Share page + +Fixes issue [#3764](https://github.com/naturalcrit/homebrewery/issues/3764) + +* [x] Fix print size when printing a zoomed document + +Fixes issue [#3744](https://github.com/naturalcrit/homebrewery/issues/3744) + +##### All + +* [x] Background code cleanup, security fixes, dev tool improvements, dependency updates, prep for upcoming features, etc. +}} + +### Wednesday 9/25/2024 - v3.15.1 + +{{taskList +##### calculuschild + +* [x] Background fixes to handle Google Drive issues + +* [x] Remove duplicate error logging + +##### calculuschild, 5e-Cleric + +* [x] Fix links in {{openSans **RECENT BREWS :fas_clock_rotate_left:**}} and user {{openSans **BREWS :fas_beer_mug_empty:**}} pointing to trashed Google Drive files after transferring from Google to Homebrewery storage + +Fixes issue [#3776](https://github.com/naturalcrit/homebrewery/issues/3776) +}} + +\page + ### Wednesday 9/04/2024 - v3.15.0 {{taskList diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 17a7d9345..dba99638b 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -1,7 +1,7 @@ /*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ require('./brewRenderer.less'); const React = require('react'); -const { useState, useRef, useCallback } = React; +const { useState, useRef, useCallback, useMemo } = React; const _ = require('lodash'); const MarkdownLegacy = require('naturalcrit/markdownLegacy.js'); @@ -44,7 +44,7 @@ const BrewPage = (props)=>{ //v=====--------------------< Brew Renderer Component >-------------------=====v// -const renderedPages = []; +let renderedPages = []; let rawPages = []; const BrewRenderer = (props)=>{ @@ -179,6 +179,9 @@ const BrewRenderer = (props)=>{ styleObject.backgroundImage = `url("data:image/svg+xml;utf8,${global.config.deployment}")`; } + const renderedStyle = useMemo(()=> renderStyle(), [props.style?.length, props.themeBundle]); + renderedPages = useMemo(() => renderPages(), [props.text?.length]); + return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -214,9 +217,9 @@ const BrewRenderer = (props)=>{ {state.isMounted && <> - {renderStyle()} + {renderedStyle}
- {renderPages()} + {renderedPages}
} diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx index cba6a629c..f4e0b1c95 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx @@ -4,7 +4,7 @@ const _ = require('lodash'); import Dialog from '../../../components/dialog.jsx'; -const DISMISS_KEY = 'dismiss_notification04-09-24'; +const DISMISS_KEY = 'dismiss_notification01-10-24'; const DISMISS_BUTTON = ; const NotificationPopup = ()=>{ diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 13c22fb81..9fef72cbb 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -314,7 +314,7 @@ const Editor = createClass({ }, brewJump : function(targetPage=this.props.currentEditorCursorPageNum, smooth=true){ - if(!window || isJumping) + if(!window || !this.isText() || isJumping) return; // Get current brewRenderer scroll position and calculate target position @@ -355,7 +355,7 @@ const Editor = createClass({ }, sourceJump : function(targetPage=this.props.currentBrewRendererPageNum, smooth=true){ - if(!this.isText || isJumping) + if(!this.isText() || isJumping) return; const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/; diff --git a/package-lock.json b/package-lock.json index 02db16114..e3fb0ad29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "homebrewery", - "version": "3.15.0", + "version": "3.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebrewery", - "version": "3.15.0", + "version": "3.16.0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -45,7 +45,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router-dom": "6.26.2", + "react-router-dom": "6.27.0", "sanitize-filename": "1.6.3", "superagent": "^10.1.0", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" @@ -59,7 +59,7 @@ "jest": "^29.7.0", "jest-expect-message": "^1.1.3", "postcss-less": "^6.0.0", - "stylelint": "^16.9.0", + "stylelint": "^16.10.0", "stylelint-config-recess-order": "^5.1.1", "stylelint-config-recommended": "^14.0.1", "supertest": "^7.0.0" @@ -2900,9 +2900,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", - "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", + "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", "engines": { "node": ">=14.0.0" } @@ -5037,23 +5037,21 @@ } }, "node_modules/css-functions-list": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", - "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", + "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12 || >=16" } }, "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.0.tgz", + "integrity": "sha512-o88DVQ6GzsABn1+6+zo2ct801dBO5OASVyxbbvA2W20ue2puSh/VOuqUj90eUeMSX/xqGqBmOKiRQN7tJOuBXw==", "dev": true, - "license": "MIT", "dependencies": { - "mdn-data": "2.0.30", + "mdn-data": "2.10.0", "source-map-js": "^1.0.1" }, "engines": { @@ -5134,12 +5132,11 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "license": "MIT", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -10358,11 +10355,10 @@ } }, "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.10.0.tgz", + "integrity": "sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==", + "dev": true }, "node_modules/media-typer": { "version": "0.3.0", @@ -10787,12 +10783,6 @@ } } }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", @@ -10815,10 +10805,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nan": { "version": "2.20.0", @@ -11539,10 +11528,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "license": "ISC" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -11665,9 +11653,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -11685,8 +11673,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -11712,9 +11700,9 @@ "dev": true }, "node_modules/postcss-safe-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", - "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", "dev": true, "funding": [ { @@ -11730,7 +11718,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "engines": { "node": ">=18.0" }, @@ -12073,11 +12060,11 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", - "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", + "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", "dependencies": { - "@remix-run/router": "1.19.2" + "@remix-run/router": "1.20.0" }, "engines": { "node": ">=14.0.0" @@ -12087,12 +12074,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", - "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", + "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", "dependencies": { - "@remix-run/router": "1.19.2", - "react-router": "6.26.2" + "@remix-run/router": "1.20.0", + "react-router": "6.27.0" }, "engines": { "node": ">=14.0.0" @@ -12624,11 +12611,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", @@ -13032,11 +13014,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -13418,9 +13399,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.9.0.tgz", - "integrity": "sha512-31Nm3WjxGOBGpQqF43o3wO9L5AC36TPIe6030Lnm13H3vDMTcS21DrLh69bMX+DBilKqMMVLian4iG6ybBoNRQ==", + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.10.0.tgz", + "integrity": "sha512-z/8X2rZ52dt2c0stVwI9QL2AFJhLhbPkyfpDFcizs200V/g7v+UYY6SNcB9hKOLcDDX/yGLDsY/pX08sLkz9xQ==", "dev": true, "funding": [ { @@ -13441,17 +13422,17 @@ "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.2", - "css-tree": "^2.3.1", - "debug": "^4.3.6", + "css-functions-list": "^3.2.3", + "css-tree": "^3.0.0", + "debug": "^4.3.7", "fast-glob": "^3.3.2", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.0.0", + "file-entry-cache": "^9.1.0", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^5.3.2", + "ignore": "^6.0.2", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.34.0", @@ -13460,14 +13441,13 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.0.1", - "postcss": "^8.4.41", + "postcss": "^8.4.47", "postcss-resolve-nested-selector": "^0.1.6", - "postcss-safe-parser": "^7.0.0", + "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", - "strip-ansi": "^7.1.0", "supports-hyperlinks": "^3.1.0", "svg-tags": "^1.0.0", "table": "^6.8.2", @@ -13529,19 +13509,6 @@ "stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1" } }, - "node_modules/stylelint/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/stylelint/node_modules/balanced-match": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", @@ -13550,11 +13517,10 @@ "license": "MIT" }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", - "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", + "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^5.0.0" }, @@ -13567,7 +13533,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.3.1", "keyv": "^4.5.4" @@ -13576,6 +13541,15 @@ "node": ">=18" } }, + "node_modules/stylelint/node_modules/ignore": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-6.0.2.tgz", + "integrity": "sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -13599,22 +13573,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/stylelint/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/stylelint/node_modules/write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", diff --git a/package.json b/package.json index 5d0be26ef..a4c6ee38a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebrewery", "description": "Create authentic looking D&D homebrews using only markdown", - "version": "3.15.0", + "version": "3.16.0", "engines": { "npm": "^10.2.x", "node": "^20.17.x" @@ -121,7 +121,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router-dom": "6.26.2", + "react-router-dom": "6.27.0", "sanitize-filename": "1.6.3", "superagent": "^10.1.0", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" @@ -135,7 +135,7 @@ "jest": "^29.7.0", "jest-expect-message": "^1.1.3", "postcss-less": "^6.0.0", - "stylelint": "^16.9.0", + "stylelint": "^16.10.0", "stylelint-config-recess-order": "^5.1.1", "stylelint-config-recommended": "^14.0.1", "supertest": "^7.0.0" diff --git a/server/app.js b/server/app.js index bf6ec37fb..5f3a35150 100644 --- a/server/app.js +++ b/server/app.js @@ -10,7 +10,6 @@ const app = express(); const config = require('./config.js'); const fs = require('fs-extra'); - const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = require('./homebrew.api.js'); const GoogleActions = require('./googleActions.js'); const serveCompressedStaticAssets = require('./static-assets.mv.js'); @@ -32,6 +31,8 @@ const sanitizeBrew = (brew, accessType)=>{ return brew; }; +app.set('trust proxy', 1 /* number of proxies between user and server */) + app.use('/', serveCompressedStaticAssets(`build`)); app.use(require('./middleware/content-negotiation.js')); app.use(require('body-parser').json({ limit: '25mb' })); @@ -257,6 +258,8 @@ app.get('/user/:username', async (req, res, next)=>{ console.log(err); }); + brews.forEach(brew => brew.stubbed = true); //All brews from MongoDB are "stubbed" + if(ownAccount && req?.account?.googleId){ const auth = await GoogleActions.authCheck(req.account, res); let googleBrews = await GoogleActions.listGoogleBrews(auth) @@ -264,12 +267,12 @@ app.get('/user/:username', async (req, res, next)=>{ console.error(err); }); + // If stub matches file from Google, use Google metadata over stub metadata if(googleBrews && googleBrews.length > 0) { for (const brew of brews.filter((brew)=>brew.googleId)) { const match = googleBrews.findIndex((b)=>b.editId === brew.editId); if(match !== -1) { brew.googleId = googleBrews[match].googleId; - brew.stubbed = true; brew.pageCount = googleBrews[match].pageCount; brew.renderer = googleBrews[match].renderer; brew.version = googleBrews[match].version; @@ -278,6 +281,7 @@ app.get('/user/:username', async (req, res, next)=>{ } } + //Remaining unstubbed google brews display current user as author googleBrews = googleBrews.map((brew)=>({ ...brew, authors: [req.account.username] })); brews = _.concat(brews, googleBrews); } @@ -380,7 +384,7 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r app.get('/account', asyncHandler(async (req, res, next)=>{ const data = {}; data.title = 'Account Information Page'; - + if(!req.account) { res.set('WWW-Authenticate', 'Bearer realm="Authorization Required"'); const error = new Error('No valid account'); @@ -394,22 +398,12 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ let googleCount = []; if(req.account) { if(req.account.googleId) { - try { - auth = await GoogleActions.authCheck(req.account, res, false); - } catch (e) { - auth = undefined; - console.log('Google auth check failed!'); - console.log(e); - } - if(auth.credentials.access_token) { - try { - googleCount = await GoogleActions.listGoogleBrews(auth); - } catch (e) { - googleCount = undefined; - console.log('List Google files failed!'); - console.log(e); - } - } + auth = await GoogleActions.authCheck(req.account, res, false) + + googleCount = await GoogleActions.listGoogleBrews(auth) + .catch((err)=>{ + console.error(err); + }); } const query = { authors: req.account.username, googleId: { $exists: false } }; @@ -423,7 +417,7 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ username : req.account.username, issued : req.account.issued, googleId : Boolean(req.account.googleId), - authCheck : Boolean(req.account.googleId && auth.credentials.access_token), + authCheck : Boolean(req.account.googleId && auth?.credentials.access_token), mongoCount : mongoCount, googleCount : googleCount?.length }; @@ -468,8 +462,8 @@ app.get('/vault', asyncHandler(async(req, res, next)=>{ //Send rendered page app.use(asyncHandler(async (req, res, next)=>{ - if(!req.route) return res.redirect('/'); // Catch-all for invalid routes - + if (!req.route) return res.redirect('/'); // Catch-all for invalid routes + const page = await renderPage(req, res); if(!page) return; res.send(page); diff --git a/server/googleActions.js b/server/googleActions.js index b8e96e652..bc97551ee 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -25,6 +25,15 @@ if(!config.get('service_account')){ const defaultAuth = serviceAuth || config.get('google_api_key'); +const retryConfig = { + retry: 3, // Number of retry attempts + retryDelay: 100, // Initial delay in milliseconds + retryDelayMultiplier: 2, // Multiplier for exponential backoff + maxRetryDelay: 32000, // Maximum delay in milliseconds + httpMethodsToRetry: ['PATCH'], // Only retry PATCH requests + statusCodesToRetry: [[429, 429]], // Only retry on 429 status code +}; + const GoogleActions = { authCheck : (account, res, updateTokens=true)=>{ @@ -112,9 +121,7 @@ const GoogleActions = { }) .catch((err)=>{ console.log(`Error Listing Google Brews`); - console.error(err); throw (err); - //TODO: Should break out here, but continues on for some reason. }); fileList.push(...obj.data.files); NextPageToken = obj.data.nextPageToken; @@ -147,7 +154,7 @@ const GoogleActions = { return brews; }, - updateGoogleBrew : async (brew)=>{ + updateGoogleBrew : async (brew, userIp)=>{ const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth }); await drive.files.update({ @@ -168,7 +175,11 @@ const GoogleActions = { media : { mimeType : 'text/plain', body : brew.text - } + }, + headers: { + 'X-Forwarded-For': userIp, // Set the X-Forwarded-For header + }, + retryConfig }) .catch((err)=>{ console.log('Error saving to google'); diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 22e2cee7b..213b341ca 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -353,7 +353,7 @@ const api = { if(!brew.googleId) return; } else if(brew.googleId) { // If the google id exists and no other actions are being performed, update the google brew - const updated = await GoogleActions.updateGoogleBrew(api.excludeGoogleProps(brew)); + const updated = await GoogleActions.updateGoogleBrew(api.excludeGoogleProps(brew), req.ip); if(!updated) return; }