0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-08 22:32:41 +00:00

Merge branch 'master' into dependabot/npm_and_yarn/mongoose-8.8.2

This commit is contained in:
Trevor Buckner
2024-11-21 11:21:55 -05:00
committed by GitHub
49 changed files with 237 additions and 225 deletions

View File

@@ -5,7 +5,7 @@ const { useState, useRef, useCallback, useMemo } = React;
const _ = require('lodash'); const _ = require('lodash');
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js'); const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
const ErrorBar = require('./errorBar/errorBar.jsx'); const ErrorBar = require('./errorBar/errorBar.jsx');
const ToolBar = require('./toolBar/toolBar.jsx'); const ToolBar = require('./toolBar/toolBar.jsx');

View File

@@ -1,6 +1,6 @@
require('./notificationPopup.less'); require('./notificationPopup.less');
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
const request = require('../../utils/request-middleware.js'); import request from '../../utils/request-middleware.js';
import Dialog from '../../../components/dialog.jsx'; import Dialog from '../../../components/dialog.jsx';

View File

@@ -4,7 +4,7 @@ const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const _ = require('lodash'); const _ = require('lodash');
const dedent = require('dedent-tabs').default; const dedent = require('dedent-tabs').default;
const Markdown = require('../../../shared/naturalcrit/markdown.js'); import Markdown from '../../../shared/naturalcrit/markdown.js';
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx'); const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx'); const SnippetBar = require('./snippetbar/snippetbar.jsx');

View File

@@ -3,7 +3,7 @@ require('./metadataEditor.less');
const React = require('react'); const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const _ = require('lodash'); const _ = require('lodash');
const request = require('../../utils/request-middleware.js'); import request from '../../utils/request-middleware.js';
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Combobox = require('client/components/combobox.jsx'); const Combobox = require('client/components/combobox.jsx');
const TagInput = require('../tagInput/tagInput.jsx'); const TagInput = require('../tagInput/tagInput.jsx');

View File

@@ -2,7 +2,7 @@ require('./brewItem.less');
const React = require('react'); const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const moment = require('moment'); const moment = require('moment');
const request = require('../../../../utils/request-middleware.js'); import request from '../../../../utils/request-middleware.js';
const googleDriveIcon = require('../../../../googleDrive.svg'); const googleDriveIcon = require('../../../../googleDrive.svg');
const homebreweryIcon = require('../../../../thumbnail.png'); const homebreweryIcon = require('../../../../thumbnail.png');

View File

@@ -4,7 +4,7 @@ const React = require('react');
const _ = require('lodash'); const _ = require('lodash');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const request = require('../../utils/request-middleware.js'); import request from '../../utils/request-middleware.js';
const { Meta } = require('vitreum/headtags'); const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
@@ -23,7 +23,7 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const LockNotification = require('./lockNotification/lockNotification.jsx'); const LockNotification = require('./lockNotification/lockNotification.jsx');
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js'); const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js'); const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js');

View File

@@ -1,7 +1,7 @@
require('./errorPage.less'); require('./errorPage.less');
const React = require('react'); const React = require('react');
const UIPage = require('../basePages/uiPage/uiPage.jsx'); const UIPage = require('../basePages/uiPage/uiPage.jsx');
const Markdown = require('../../../../shared/naturalcrit/markdown.js'); import Markdown from '../../../../shared/naturalcrit/markdown.js';
const ErrorIndex = require('./errors/errorIndex.js'); const ErrorIndex = require('./errors/errorIndex.js');
const ErrorPage = ({ brew })=>{ const ErrorPage = ({ brew })=>{

View File

@@ -2,7 +2,7 @@ require('./homePage.less');
const React = require('react'); const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const cx = require('classnames'); const cx = require('classnames');
const request = require('../../utils/request-middleware.js'); import request from '../../utils/request-middleware.js';
const { Meta } = require('vitreum/headtags'); const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');

View File

@@ -2,9 +2,9 @@
require('./newPage.less'); require('./newPage.less');
const React = require('react'); const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const request = require('../../utils/request-middleware.js'); import request from '../../utils/request-middleware.js';
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const PrintNavItem = require('../../navbar/print.navitem.jsx'); const PrintNavItem = require('../../navbar/print.navitem.jsx');

View File

@@ -15,7 +15,7 @@ const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx'); const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx');
const ErrorIndex = require('../errorPage/errors/errorIndex.js'); const ErrorIndex = require('../errorPage/errors/errorIndex.js');
const request = require('../../utils/request-middleware.js'); import request from '../../utils/request-middleware.js';
const VaultPage = (props)=>{ const VaultPage = (props)=>{
const [pageState, setPageState] = useState(parseInt(props.query.page) || 1); const [pageState, setPageState] = useState(parseInt(props.query.page) || 1);

View File

@@ -1,4 +1,4 @@
const request = require('superagent'); import request from 'superagent';
const addHeader = (request)=>request.set('Homebrewery-Version', global.version); const addHeader = (request)=>request.set('Homebrewery-Version', global.version);
@@ -9,4 +9,4 @@ const requestMiddleware = {
delete : (path)=>addHeader(request.delete(path)), delete : (path)=>addHeader(request.delete(path)),
}; };
module.exports = requestMiddleware; export default requestMiddleware;

View File

@@ -8,6 +8,8 @@ const template = async function(name, title='', props = {}){
}); });
const ogMetaTags = ogTags.join('\n'); const ogMetaTags = ogTags.join('\n');
const ssrModule = await import(`../build/${name}/ssr.cjs`);
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html> <html>
<head> <head>
@@ -21,7 +23,7 @@ const template = async function(name, title='', props = {}){
<title>${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}</title> <title>${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}</title>
</head> </head>
<body> <body>
<main id="reactRoot">${require(`../build/${name}/ssr.js`)(props)}</main> <main id="reactRoot">${ssrModule.default(props)}</main>
<script src=${`/${name}/bundle.js`}></script> <script src=${`/${name}/bundle.js`}></script>
<script>start_app(${JSON.stringify(props)})</script> <script>start_app(${JSON.stringify(props)})</script>
</body> </body>
@@ -29,4 +31,4 @@ const template = async function(name, title='', props = {}){
`; `;
}; };
module.exports = template; export default template;

23
package-lock.json generated
View File

@@ -52,6 +52,7 @@
}, },
"devDependencies": { "devDependencies": {
"@stylistic/stylelint-plugin": "^3.1.1", "@stylistic/stylelint-plugin": "^3.1.1",
"babel-plugin-transform-import-meta": "^2.2.1",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-plugin-jest": "^28.9.0", "eslint-plugin-jest": "^28.9.0",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.2",
@@ -559,6 +560,7 @@
"version": "7.26.0", "version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
"integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-plugin-utils": "^7.25.9" "@babel/helper-plugin-utils": "^7.25.9"
}, },
@@ -3881,6 +3883,27 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
} }
}, },
"node_modules/babel-plugin-transform-import-meta": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz",
"integrity": "sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==",
"dev": true,
"license": "BSD",
"dependencies": {
"@babel/template": "^7.4.4",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@babel/core": "^7.10.0"
}
},
"node_modules/babel-plugin-transform-import-meta/node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/babel-preset-current-node-syntax": { "node_modules/babel-preset-current-node-syntax": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",

View File

@@ -2,6 +2,7 @@
"name": "homebrewery", "name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown", "description": "Create authentic looking D&D homebrews using only markdown",
"version": "3.16.0", "version": "3.16.0",
"type": "module",
"engines": { "engines": {
"npm": "^10.2.x", "npm": "^10.2.x",
"node": "^20.18.x" "node": "^20.18.x"
@@ -83,7 +84,8 @@
"@babel/preset-react" "@babel/preset-react"
], ],
"plugins": [ "plugins": [
"@babel/plugin-transform-runtime" "@babel/plugin-transform-runtime",
"babel-plugin-transform-import-meta"
] ]
}, },
"dependencies": { "dependencies": {
@@ -129,6 +131,7 @@
}, },
"devDependencies": { "devDependencies": {
"@stylistic/stylelint-plugin": "^3.1.1", "@stylistic/stylelint-plugin": "^3.1.1",
"babel-plugin-transform-import-meta": "^2.2.1",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-plugin-jest": "^28.9.0", "eslint-plugin-jest": "^28.9.0",
"eslint-plugin-react": "^7.37.2", "eslint-plugin-react": "^7.37.2",

View File

@@ -1,13 +1,14 @@
const fs = require('fs-extra');
const Proj = require('./project.json');
const { pack } = require('vitreum'); import fs from 'fs-extra';
import Proj from './project.json' with { type: 'json' };
import vitreum from 'vitreum';
const { pack } = vitreum;
import lessTransform from 'vitreum/transforms/less.js';
import assetTransform from 'vitreum/transforms/asset.js';
const isDev = !!process.argv.find((arg)=>arg=='--dev'); const isDev = !!process.argv.find((arg)=>arg=='--dev');
const lessTransform = require('vitreum/transforms/less.js');
const assetTransform = require('vitreum/transforms/asset.js');
//const Meta = require('vitreum/headtags');
const transforms = { const transforms = {
'.less' : lessTransform, '.less' : lessTransform,
'*' : assetTransform('./build') '*' : assetTransform('./build')
@@ -17,7 +18,7 @@ const build = async ({ bundle, render, ssr })=>{
const css = await lessTransform.generate({ paths: './shared' }); const css = await lessTransform.generate({ paths: './shared' });
await fs.outputFile('./build/admin/bundle.css', css); await fs.outputFile('./build/admin/bundle.css', css);
await fs.outputFile('./build/admin/bundle.js', bundle); await fs.outputFile('./build/admin/bundle.js', bundle);
await fs.outputFile('./build/admin/ssr.js', ssr); await fs.outputFile('./build/admin/ssr.cjs', ssr);
}; };
fs.emptyDirSync('./build/admin'); fs.emptyDirSync('./build/admin');

View File

@@ -1,14 +1,15 @@
const fs = require('fs-extra'); import fs from 'fs-extra';
const zlib = require('zlib'); import zlib from 'zlib';
const Proj = require('./project.json'); import Proj from './project.json' with { type: 'json' };
import vitreum from 'vitreum';
const { pack, watchFile, livereload } = vitreum;
const { pack, watchFile, livereload } = require('vitreum'); import lessTransform from 'vitreum/transforms/less.js';
const isDev = !!process.argv.find((arg)=>arg=='--dev'); import assetTransform from 'vitreum/transforms/asset.js';
import babel from '@babel/core';
import less from 'less';
const lessTransform = require('vitreum/transforms/less.js'); const isDev = !!process.argv.find((arg) => arg === '--dev');
const assetTransform = require('vitreum/transforms/asset.js');
const babel = require('@babel/core');
const less = require('less');
const babelify = async (code)=>(await babel.transformAsync(code, { presets: [['@babel/preset-env', { 'exclude': ['proposal-dynamic-import'] }], '@babel/preset-react'], plugins: ['@babel/plugin-transform-runtime'] })).code; const babelify = async (code)=>(await babel.transformAsync(code, { presets: [['@babel/preset-env', { 'exclude': ['proposal-dynamic-import'] }], '@babel/preset-react'], plugins: ['@babel/plugin-transform-runtime'] })).code;
@@ -24,7 +25,7 @@ const build = async ({ bundle, render, ssr })=>{
//css = `@layer bundle {\n${css}\n}`; //css = `@layer bundle {\n${css}\n}`;
await fs.outputFile('./build/homebrew/bundle.css', css); await fs.outputFile('./build/homebrew/bundle.css', css);
await fs.outputFile('./build/homebrew/bundle.js', bundle); await fs.outputFile('./build/homebrew/bundle.js', bundle);
await fs.outputFile('./build/homebrew/ssr.js', ssr); await fs.outputFile('./build/homebrew/ssr.cjs', ssr);
await fs.copy('./client/homebrew/favicon.ico', './build/assets/favicon.ico'); await fs.copy('./client/homebrew/favicon.ico', './build/assets/favicon.ico');
@@ -51,7 +52,7 @@ fs.emptyDirSync('./build');
const themes = { Legacy: {}, V3: {} }; const themes = { Legacy: {}, V3: {} };
let themeFiles = fs.readdirSync('./themes/Legacy'); let themeFiles = fs.readdirSync('./themes/Legacy');
for (dir of themeFiles) { for (let dir of themeFiles) {
const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString()); const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString());
themeData.path = dir; themeData.path = dir;
themes.Legacy[dir] = (themeData); themes.Legacy[dir] = (themeData);
@@ -68,7 +69,7 @@ fs.emptyDirSync('./build');
} }
themeFiles = fs.readdirSync('./themes/V3'); themeFiles = fs.readdirSync('./themes/V3');
for (dir of themeFiles) { for (let dir of themeFiles) {
const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString()); const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString());
themeData.path = dir; themeData.path = dir;
themes.V3[dir] = (themeData); themes.V3[dir] = (themeData);
@@ -104,14 +105,14 @@ fs.emptyDirSync('./build');
const editorThemesBuildDir = './build/homebrew/cm-themes'; const editorThemesBuildDir = './build/homebrew/cm-themes';
await fs.copy('./node_modules/codemirror/theme', editorThemesBuildDir); await fs.copy('./node_modules/codemirror/theme', editorThemesBuildDir);
await fs.copy('./themes/codeMirror/customThemes', editorThemesBuildDir); await fs.copy('./themes/codeMirror/customThemes', editorThemesBuildDir);
editorThemeFiles = fs.readdirSync(editorThemesBuildDir); const editorThemeFiles = fs.readdirSync(editorThemesBuildDir);
const editorThemeFile = './themes/codeMirror/editorThemes.json'; const editorThemeFile = './themes/codeMirror/editorThemes.json';
if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile); if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile);
const stream = fs.createWriteStream(editorThemeFile, { flags: 'a' }); const stream = fs.createWriteStream(editorThemeFile, { flags: 'a' });
stream.write('[\n"default"'); stream.write('[\n"default"');
for (themeFile of editorThemeFiles) { for (let themeFile of editorThemeFiles) {
stream.write(`,\n"${themeFile.slice(0, -4)}"`); stream.write(`,\n"${themeFile.slice(0, -4)}"`);
} }
stream.write('\n]\n'); stream.write('\n]\n');

View File

@@ -1,12 +1,12 @@
const DB = require('./server/db.js'); import DB from './server/db.js';
const server = require('./server/app.js'); import server from './server/app.js';
const config = require('./server/config.js'); import config from './server/config.js';
DB.connect(config).then(()=>{ DB.connect(config).then(()=>{
// Ensure that we have successfully connected to the database // Ensure that we have successfully connected to the database
// before launching server // before launching server
const PORT = process.env.PORT || config.get('web_port') || 8000; const PORT = process.env.PORT || config.get('web_port') || 8000;
server.app.listen(PORT, ()=>{ server.listen(PORT, ()=>{
const reset = '\x1b[0m'; // Reset to default style const reset = '\x1b[0m'; // Reset to default style
const bright = '\x1b[1m'; // Bright (bold) style const bright = '\x1b[1m'; // Bright (bold) style
const cyan = '\x1b[36m'; // Cyan color const cyan = '\x1b[36m'; // Cyan color

View File

@@ -1,13 +1,15 @@
const HomebrewModel = require('./homebrew.model.js').model; import {model as HomebrewModel } from './homebrew.model.js';
const NotificationModel = require('./notifications.model.js').model; import {model as NotificationModel } from './notifications.model.js';
const router = require('express').Router(); import express from 'express';
const Moment = require('moment'); import Moment from 'moment';
const templateFn = require('../client/template.js'); import zlib from 'zlib';
const zlib = require('zlib'); import templateFn from '../client/template.js';
const HomebrewAPI = require('./homebrew.api.js'); import HomebrewAPI from './homebrew.api.js';
const asyncHandler = require('express-async-handler'); import asyncHandler from 'express-async-handler';
const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); import { splitTextStyleAndMetadata } from '../shared/helpers.js';
const router = express.Router();
process.env.ADMIN_USER = process.env.ADMIN_USER || 'admin'; process.env.ADMIN_USER = process.env.ADMIN_USER || 'admin';
process.env.ADMIN_PASS = process.env.ADMIN_PASS || 'password3'; process.env.ADMIN_PASS = process.env.ADMIN_PASS || 'password3';
@@ -190,4 +192,4 @@ router.get('/admin', mw.adminOnly, (req, res)=>{
}); });
}); });
module.exports = router; export default router;

View File

@@ -1,9 +1,10 @@
const supertest = require('supertest'); import supertest from 'supertest';
import HBApp from './app.js';
import {model as NotificationModel } from './notifications.model.js';
const app = supertest.agent(require('app.js').app)
.set('X-Forwarded-Proto', 'https');
const NotificationModel = require('./notifications.model.js').model; // Mimic https responses to avoid being redirected all the time
const app = supertest.agent(HBApp).set('X-Forwarded-Proto', 'https');
describe('Tests for admin api', ()=>{ describe('Tests for admin api', ()=>{
afterEach(()=>{ afterEach(()=>{

View File

@@ -1,25 +1,41 @@
/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ /*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/
// Set working directory to project root // Set working directory to project root
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import packageJSON from './../package.json' with { type: "json" };
const __dirname = dirname(fileURLToPath(import.meta.url));
process.chdir(`${__dirname}/..`); process.chdir(`${__dirname}/..`);
const version = packageJSON.version;
import _ from 'lodash';
import jwt from 'jwt-simple';
import express from 'express';
import yaml from 'js-yaml';
import config from './config.js';
import fs from 'fs-extra';
const _ = require('lodash');
const jwt = require('jwt-simple');
const express = require('express');
const yaml = require('js-yaml');
const app = express(); const app = express();
const config = require('./config.js');
const fs = require('fs-extra');
const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = require('./homebrew.api.js'); import api from './homebrew.api.js';
const GoogleActions = require('./googleActions.js'); const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = api;
const serveCompressedStaticAssets = require('./static-assets.mv.js'); import adminApi from './admin.api.js';
const sanitizeFilename = require('sanitize-filename'); import vaultApi from './vault.api.js';
const asyncHandler = require('express-async-handler'); import GoogleActions from './googleActions.js';
const templateFn = require('./../client/template.js'); import serveCompressedStaticAssets from './static-assets.mv.js';
import sanitizeFilename from 'sanitize-filename';
import asyncHandler from 'express-async-handler';
import templateFn from '../client/template.js';
import {model as HomebrewModel } from './homebrew.model.js';
const { DEFAULT_BREW } = require('./brewDefaults.js'); import { DEFAULT_BREW } from './brewDefaults.js';
import { splitTextStyleAndMetadata } from '../shared/helpers.js';
const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); //==== Middleware Imports ====//
import contentNegotiation from './middleware/content-negotiation.js';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import forceSSL from './forcessl.mw.js';
const sanitizeBrew = (brew, accessType)=>{ const sanitizeBrew = (brew, accessType)=>{
@@ -34,10 +50,10 @@ const sanitizeBrew = (brew, accessType)=>{
app.set('trust proxy', 1 /* number of proxies between user and server */) app.set('trust proxy', 1 /* number of proxies between user and server */)
app.use('/', serveCompressedStaticAssets(`build`)); app.use('/', serveCompressedStaticAssets(`build`));
app.use(require('./middleware/content-negotiation.js')); app.use(contentNegotiation);
app.use(require('body-parser').json({ limit: '25mb' })); app.use(bodyParser.json({ limit: '25mb' }));
app.use(require('cookie-parser')()); app.use(cookieParser());
app.use(require('./forcessl.mw.js')); app.use(forceSSL);
//Account Middleware //Account Middleware
app.use((req, res, next)=>{ app.use((req, res, next)=>{
@@ -57,15 +73,14 @@ app.use((req, res, next)=>{
}); });
app.use(homebrewApi); app.use(homebrewApi);
app.use(require('./admin.api.js')); app.use(adminApi);
app.use(require('./vault.api.js')); app.use(vaultApi);
const HomebrewModel = require('./homebrew.model.js').model; const welcomeText = fs.readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const welcomeText = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8'); const welcomeTextLegacy = fs.readFileSync('client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8');
const welcomeTextLegacy = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8'); const migrateText = fs.readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8');
const migrateText = require('fs').readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8'); const changelogText = fs.readFileSync('changelog.md', 'utf8');
const changelogText = require('fs').readFileSync('changelog.md', 'utf8'); const faqText = fs.readFileSync('faq.md', 'utf8');
const faqText = require('fs').readFileSync('faq.md', 'utf8');
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);}; String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
@@ -479,7 +494,7 @@ const renderPage = async (req, res)=>{
deployment : config.get('heroku_app_name') ?? '' deployment : config.get('heroku_app_name') ?? ''
}; };
const props = { const props = {
version : require('./../package.json').version, version : version,
url : req.customUrl || req.originalUrl, url : req.customUrl || req.originalUrl,
brew : req.brew, brew : req.brew,
brews : req.brews, brews : req.brews,
@@ -556,6 +571,4 @@ app.use((req, res)=>{
}); });
//^=====--------------------------------------=====^// //^=====--------------------------------------=====^//
module.exports = { export default app;
app : app
};

View File

@@ -1,4 +1,4 @@
const _ = require('lodash'); import _ from 'lodash';
// Default properties for newly-created brews // Default properties for newly-created brews
const DEFAULT_BREW = { const DEFAULT_BREW = {
@@ -32,7 +32,7 @@ const DEFAULT_BREW_LOAD = _.defaults(
}, },
DEFAULT_BREW); DEFAULT_BREW);
module.exports = { export {
DEFAULT_BREW, DEFAULT_BREW,
DEFAULT_BREW_LOAD DEFAULT_BREW_LOAD
}; };

View File

@@ -1,5 +1,7 @@
module.exports = require('nconf') import nconf from 'nconf';
.argv()
.env({ lowerCase: true }) export default nconf
.file('environment', { file: `config/${process.env.NODE_ENV}.json` }) .argv()
.file('defaults', { file: 'config/default.json' }); .env({ lowerCase: true })
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
.file('defaults', { file: 'config/default.json' });

View File

@@ -5,7 +5,7 @@
// reused by both the main application and all tests which require database // reused by both the main application and all tests which require database
// connection. // connection.
const Mongoose = require('mongoose'); import Mongoose from 'mongoose';
const getMongoDBURL = (config)=>{ const getMongoDBURL = (config)=>{
return config.get('mongodb_uri') || return config.get('mongodb_uri') ||
@@ -31,7 +31,7 @@ const connect = async (config)=>{
.catch((error)=>handleConnectionError(error)); .catch((error)=>handleConnectionError(error));
}; };
module.exports = { export default {
connect : connect, connect,
disconnect : disconnect disconnect
}; };

View File

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

View File

@@ -1,8 +1,9 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const googleDrive = require('@googleapis/drive'); import googleDrive from '@googleapis/drive';
const { nanoid } = require('nanoid'); import { nanoid } from 'nanoid';
const token = require('./token.js'); import token from './token.js';
const config = require('./config.js'); import config from './config.js';
let serviceAuth; let serviceAuth;
if(!config.get('service_account')){ if(!config.get('service_account')){
@@ -72,7 +73,7 @@ const GoogleActions = {
getGoogleFolder : async (auth)=>{ getGoogleFolder : async (auth)=>{
const drive = googleDrive.drive({ version: 'v3', auth }); const drive = googleDrive.drive({ version: 'v3', auth });
fileMetadata = { const fileMetadata = {
'name' : 'Homebrewery', 'name' : 'Homebrewery',
'mimeType' : 'application/vnd.google-apps.folder' 'mimeType' : 'application/vnd.google-apps.folder'
}; };
@@ -344,4 +345,4 @@ const GoogleActions = {
} }
}; };
module.exports = GoogleActions; export default GoogleActions;

View File

@@ -1,18 +1,20 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const _ = require('lodash'); import _ from 'lodash';
const HomebrewModel = require('./homebrew.model.js').model; import {model as HomebrewModel} from './homebrew.model.js';
const router = require('express').Router(); import express from 'express';
const zlib = require('zlib'); import zlib from 'zlib';
const GoogleActions = require('./googleActions.js'); import GoogleActions from './googleActions.js';
const Markdown = require('../shared/naturalcrit/markdown.js'); import Markdown from '../shared/naturalcrit/markdown.js';
const yaml = require('js-yaml'); import yaml from 'js-yaml';
const asyncHandler = require('express-async-handler'); import asyncHandler from 'express-async-handler';
const { nanoid } = require('nanoid'); import { nanoid } from 'nanoid';
const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); import { splitTextStyleAndMetadata } from '../shared/helpers.js';
import checkClientVersion from './middleware/check-client-version.js';
const { DEFAULT_BREW, DEFAULT_BREW_LOAD } = require('./brewDefaults.js'); const router = express.Router();
const Themes = require('../themes/themes.json'); import { DEFAULT_BREW, DEFAULT_BREW_LOAD } from './brewDefaults.js';
import Themes from '../themes/themes.json' with { type: 'json' };
const isStaticTheme = (renderer, themeName)=>{ const isStaticTheme = (renderer, themeName)=>{
return Themes[renderer]?.[themeName] !== undefined; return Themes[renderer]?.[themeName] !== undefined;
@@ -473,7 +475,7 @@ const api = {
} }
}; };
router.use('/api', require('./middleware/check-client-version.js')); router.use('/api', checkClientVersion);
router.post('/api', asyncHandler(api.newBrew)); router.post('/api', asyncHandler(api.newBrew));
router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
@@ -481,4 +483,4 @@ router.delete('/api/:id', asyncHandler(api.deleteBrew));
router.get('/api/remove/:id', asyncHandler(api.deleteBrew)); router.get('/api/remove/:id', asyncHandler(api.deleteBrew));
router.get('/api/theme/:renderer/:id', asyncHandler(api.getThemeBundle)); router.get('/api/theme/:renderer/:id', asyncHandler(api.getThemeBundle));
module.exports = api; export default api;

View File

@@ -36,8 +36,9 @@ describe('Tests for api', ()=>{
} }
}); });
google = require('./googleActions.js'); google = require('./googleActions.js').default;
model = require('./homebrew.model.js').model; model = require('./homebrew.model.js').model;
api = require('./homebrew.api').default;
jest.mock('./googleActions.js'); jest.mock('./googleActions.js');
google.authCheck = jest.fn(()=>'client'); google.authCheck = jest.fn(()=>'client');
@@ -54,8 +55,6 @@ describe('Tests for api', ()=>{
setHeader : jest.fn(()=>{}) setHeader : jest.fn(()=>{})
}; };
api = require('./homebrew.api');
hbBrew = { hbBrew = {
text : `brew text`, text : `brew text`,
style : 'hello yes i am css', style : 'hello yes i am css',

View File

@@ -1,7 +1,8 @@
const mongoose = require('mongoose'); import mongoose from 'mongoose';
const { nanoid } = require('nanoid'); import { nanoid } from 'nanoid';
const _ = require('lodash'); import _ from 'lodash';
const zlib = require('zlib'); import zlib from 'zlib';
const HomebrewSchema = mongoose.Schema({ const HomebrewSchema = mongoose.Schema({
shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } }, shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } },
@@ -44,7 +45,7 @@ HomebrewSchema.statics.get = async function(query, fields=null){
const brew = await Homebrew.findOne(query, fields).orFail() const brew = await Homebrew.findOne(query, fields).orFail()
.catch((error)=>{throw 'Can not find brew';}); .catch((error)=>{throw 'Can not find brew';});
if(!_.isNil(brew.textBin)) { // Uncompress zipped text field if(!_.isNil(brew.textBin)) { // Uncompress zipped text field
unzipped = zlib.inflateRawSync(brew.textBin); const unzipped = zlib.inflateRawSync(brew.textBin);
brew.text = unzipped.toString(); brew.text = unzipped.toString();
} }
return brew; return brew;
@@ -62,7 +63,7 @@ HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, f
const Homebrew = mongoose.model('Homebrew', HomebrewSchema); const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
module.exports = { export {
schema : HomebrewSchema, HomebrewSchema as schema,
model : Homebrew, Homebrew as model
}; };

View File

@@ -1,6 +1,8 @@
module.exports = (req, res, next)=>{ import packageJSON from '../../package.json' with { type: "json" };
const version = packageJSON.version;
export default (req, res, next)=>{
const userVersion = req.get('Homebrewery-Version'); const userVersion = req.get('Homebrewery-Version');
const version = require('../../package.json').version;
if(userVersion != version) { if(userVersion != version) {
return res.status(412).send({ return res.status(412).send({

View File

@@ -1,8 +1,8 @@
const config = require('../config.js'); import config from '../config.js';
const nodeEnv = config.get('node_env'); const nodeEnv = config.get('node_env');
const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); const isLocalEnvironment = config.get('local_environments').includes(nodeEnv);
module.exports = (req, res, next)=>{ export default (req, res, next)=>{
const isImageRequest = req.get('Accept')?.split(',') const isImageRequest = req.get('Accept')?.split(',')
?.filter((h)=>!h.includes('q=')) ?.filter((h)=>!h.includes('q='))
?.every((h)=>/image\/.*/.test(h)); ?.every((h)=>/image\/.*/.test(h));

View File

@@ -1,41 +0,0 @@
const contentNegotiationMiddleware = require('./content-negotiation.js');
describe('content-negotiation-middleware', ()=>{
let request;
let response;
let next;
beforeEach(()=>{
request = {
get : function(key) {
return this[key];
}
};
response = {
status : jest.fn(()=>response),
send : jest.fn(()=>{})
};
next = jest.fn();
});
it('should return 406 on image request', ()=>{
contentNegotiationMiddleware({
Accept : 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
...request
}, response);
expect(response.status).toHaveBeenLastCalledWith(406);
expect(response.send).toHaveBeenCalledWith({
message : 'Request for image at this URL is not supported'
});
});
it('should call next on non-image request', ()=>{
contentNegotiationMiddleware({
Accept : 'text,image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
...request
}, response, next);
expect(next).toHaveBeenCalled();
});
});

View File

@@ -1,5 +1,5 @@
const mongoose = require('mongoose'); import mongoose from 'mongoose';
const _ = require('lodash'); import _ from 'lodash';
const NotificationSchema = new mongoose.Schema({ const NotificationSchema = new mongoose.Schema({
dismissKey : { type: String, unique: true, required: true }, dismissKey : { type: String, unique: true, required: true },
@@ -56,7 +56,7 @@ NotificationSchema.statics.getAll = async function() {
const Notification = mongoose.model('Notification', NotificationSchema); const Notification = mongoose.model('Notification', NotificationSchema);
module.exports = { export {
schema : NotificationSchema, NotificationSchema as schema,
model : Notification, Notification as model
}; };

View File

@@ -1,4 +1,4 @@
const expressStaticGzip = require('express-static-gzip'); import expressStaticGzip from 'express-static-gzip';
// Serve brotli-compressed static files if available // Serve brotli-compressed static files if available
const customCacheControlHandler=(response, path)=>{ const customCacheControlHandler=(response, path)=>{
@@ -28,4 +28,4 @@ const init=(pathToAssets)=>{
} }); } });
}; };
module.exports = init; export default init;

View File

@@ -1,7 +1,5 @@
const jwt = require('jwt-simple'); import jwt from 'jwt-simple';
import config from './config.js';
// Load configuration values
const config = require('./config.js');
// Generate an Access Token for the given User ID // Generate an Access Token for the given User ID
const generateAccessToken = (account)=>{ const generateAccessToken = (account)=>{
@@ -24,6 +22,4 @@ const generateAccessToken = (account)=>{
return token; return token;
}; };
module.exports = { export default generateAccessToken;
generateAccessToken : generateAccessToken
};

View File

@@ -1,6 +1,6 @@
const express = require('express'); import express from 'express';
const asyncHandler = require('express-async-handler'); import asyncHandler from 'express-async-handler';
const HomebrewModel = require('./homebrew.model.js').model; import {model as HomebrewModel } from './homebrew.model.js';
const router = express.Router(); const router = express.Router();
@@ -106,4 +106,4 @@ const findTotal = async (req, res)=>{
router.get('/api/vault/total', asyncHandler(findTotal)); router.get('/api/vault/total', asyncHandler(findTotal));
router.get('/api/vault', asyncHandler(findBrews)); router.get('/api/vault', asyncHandler(findBrews));
module.exports = router; export default router;

View File

@@ -1,6 +1,6 @@
const _ = require('lodash'); import _ from 'lodash';
const yaml = require('js-yaml'); import yaml from 'js-yaml';
const request = require('../client/homebrew/utils/request-middleware.js'); import request from '../client/homebrew/utils/request-middleware.js';
const splitTextStyleAndMetadata = (brew)=>{ const splitTextStyleAndMetadata = (brew)=>{
brew.text = brew.text.replaceAll('\r\n', '\n'); brew.text = brew.text.replaceAll('\r\n', '\n');
@@ -51,7 +51,7 @@ const fetchThemeBundle = async (obj, renderer, theme)=>{
})); }));
}; };
module.exports = { export {
splitTextStyleAndMetadata, splitTextStyleAndMetadata,
printCurrentBrew, printCurrentBrew,
fetchThemeBundle, fetchThemeBundle,

View File

@@ -1,19 +1,19 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const _ = require('lodash'); import _ from 'lodash';
const Marked = require('marked'); import { Parser as MathParser } from 'expr-eval';
const MarkedExtendedTables = require('marked-extended-tables'); import { marked as Marked } from 'marked';
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite'); import MarkedExtendedTables from 'marked-extended-tables';
const { gfmHeadingId: MarkedGFMHeadingId, resetHeadings: MarkedGFMResetHeadingIDs } = require('marked-gfm-heading-id'); import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite';
const { markedEmoji: MarkedEmojis } = require('marked-emoji'); import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id';
import { markedEmoji as MarkedEmojis } from 'marked-emoji';
//Icon fonts included so they can appear in emoji autosuggest dropdown //Icon fonts included so they can appear in emoji autosuggest dropdown
const diceFont = require('../../themes/fonts/iconFonts/diceFont.js'); import diceFont from '../../themes/fonts/iconFonts/diceFont.js';
const elderberryInn = require('../../themes/fonts/iconFonts/elderberryInn.js'); import elderberryInn from '../../themes/fonts/iconFonts/elderberryInn.js';
const fontAwesome = require('../../themes/fonts/iconFonts/fontAwesome.js'); import gameIcons from '../../themes/fonts/iconFonts/gameIcons.js';
const gameIcons = require('../../themes/fonts/iconFonts/gameIcons.js'); import fontAwesome from '../../themes/fonts/iconFonts/fontAwesome.js';
const MathParser = require('expr-eval').Parser; const renderer = new Marked.Renderer();
const renderer = new Marked.Renderer();
const tokenizer = new Marked.Tokenizer(); const tokenizer = new Marked.Tokenizer();
//Limit math features to simple items //Limit math features to simple items
@@ -854,7 +854,7 @@ const globalVarsList = {};
let varsQueue = []; let varsQueue = [];
let globalPageNumber = 0; let globalPageNumber = 0;
module.exports = { const Markdown = {
marked : Marked, marked : Marked,
render : (rawBrewText, pageNumber=0)=>{ render : (rawBrewText, pageNumber=0)=>{
globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
@@ -865,6 +865,7 @@ module.exports = {
} }
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`); rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`);
const opts = Marked.defaults; const opts = Marked.defaults;
rawBrewText = opts.hooks.preprocess(rawBrewText); rawBrewText = opts.hooks.preprocess(rawBrewText);
@@ -935,3 +936,6 @@ module.exports = {
return errors; return errors;
}, },
}; };
export default Markdown;

View File

@@ -1,6 +1,6 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
test('Processes the markdown within an HTML block if its just a class wrapper', function() { test('Processes the markdown within an HTML block if its just a class wrapper', function() {
const source = '<div>*Bold text*</div>'; const source = '<div>*Bold text*</div>';

View File

@@ -1,6 +1,6 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
describe('Inline Definition Lists', ()=>{ describe('Inline Definition Lists', ()=>{
test('No Term 1 Definition', function() { test('No Term 1 Definition', function() {

View File

@@ -1,4 +1,4 @@
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
const dedent = require('dedent-tabs').default; const dedent = require('dedent-tabs').default;
// Marked.js adds line returns after closing tags on some default tokens. // Marked.js adds line returns after closing tags on some default tokens.

View File

@@ -1,6 +1,6 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
describe('Hard Breaks', ()=>{ describe('Hard Breaks', ()=>{
test('Single Break', function() { test('Single Break', function() {

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const dedent = require('dedent-tabs').default; const dedent = require('dedent-tabs').default;
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
// Marked.js adds line returns after closing tags on some default tokens. // Marked.js adds line returns after closing tags on some default tokens.
// This removes those line returns for comparison sake. // This removes those line returns for comparison sake.

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-lines */ /* eslint-disable max-lines */
const dedent = require('dedent-tabs').default; const dedent = require('dedent-tabs').default;
const Markdown = require('naturalcrit/markdown.js'); import Markdown from 'naturalcrit/markdown.js';
// Marked.js adds line returns after closing tags on some default tokens. // Marked.js adds line returns after closing tags on some default tokens.
// This removes those line returns for comparison sake. // This removes those line returns for comparison sake.

View File

@@ -1,8 +1,8 @@
const supertest = require('supertest'); import supertest from 'supertest';
import HBApp from 'app.js';
// Mimic https responses to avoid being redirected all the time // Mimic https responses to avoid being redirected all the time
const app = supertest.agent(require('app.js').app) const app = supertest.agent(HBApp).set('X-Forwarded-Proto', 'https');
.set('X-Forwarded-Proto', 'https');
describe('Tests for static pages', ()=>{ describe('Tests for static pages', ()=>{
it('Home page works', ()=>{ it('Home page works', ()=>{

View File

@@ -1,4 +1,4 @@
const Markdown = require('../../../../shared/naturalcrit/markdown.js'); import Markdown from '../../../../shared/naturalcrit/markdown.js';
module.exports = { module.exports = {
createFooterFunc : function(headerSize=1){ createFooterFunc : function(headerSize=1){

View File

@@ -93,4 +93,4 @@ const diceFont = {
'df_solid_small_dot_d6_6' : 'df solid-small-dot-d6-6' 'df_solid_small_dot_d6_6' : 'df solid-small-dot-d6-6'
}; };
module.exports = diceFont; export default diceFont;

View File

@@ -206,4 +206,4 @@ const elderberryInn = {
'ei_wish' : 'ei wish' 'ei_wish' : 'ei wish'
}; };
module.exports = elderberryInn; export default elderberryInn;

View File

@@ -2051,4 +2051,4 @@ const fontAwesome = {
'fab_zhihu' : 'fab fa-zhihu' 'fab_zhihu' : 'fab fa-zhihu'
}; };
module.exports = fontAwesome; export default fontAwesome;

View File

@@ -506,4 +506,4 @@ const gameIcons = {
'gi_acid' : 'gi acid' 'gi_acid' : 'gi acid'
}; };
module.exports = gameIcons; export default gameIcons;