mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-24 16:22:44 +00:00
Merge branch 'master' into addMongoIndexes
This commit is contained in:
@@ -12,7 +12,7 @@ const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
|
||||
|
||||
const EDITOR_THEME_KEY = 'HOMEBREWERY-EDITOR-THEME';
|
||||
|
||||
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m;
|
||||
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n]*})?$)/m;
|
||||
const SNIPPETBREAK_REGEX_V3 = /^\\snippet\ .*$/;
|
||||
const DEFAULT_STYLE_TEXT = dedent`
|
||||
/*=======--- Example CSS styling ---=======*/
|
||||
|
||||
@@ -5,33 +5,45 @@ const { splitTextStyleAndMetadata } = require('../../../shared/helpers.js'); //
|
||||
|
||||
const BREWKEY = 'homebrewery-new';
|
||||
const STYLEKEY = 'homebrewery-new-style';
|
||||
const METAKEY = 'homebrewery-new-meta';
|
||||
const METAKEY = 'homebrewery-new-meta';
|
||||
|
||||
const NewBrew = ()=>{
|
||||
const handleFileChange = (e)=>{
|
||||
const file = e.target.files[0];
|
||||
if(file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e)=>{
|
||||
const fileContent = e.target.result;
|
||||
const newBrew = {
|
||||
text : fileContent,
|
||||
style : ''
|
||||
};
|
||||
if(fileContent.startsWith('```metadata')) {
|
||||
splitTextStyleAndMetadata(newBrew); // Modify newBrew directly
|
||||
localStorage.setItem(BREWKEY, newBrew.text);
|
||||
localStorage.setItem(STYLEKEY, newBrew.style);
|
||||
localStorage.setItem(METAKEY, JSON.stringify(_.pick(newBrew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])));
|
||||
window.location.href = '/new';
|
||||
} else {
|
||||
alert('This file is invalid, please, enter a valid file');
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
if(!file) return;
|
||||
|
||||
const currentNew = localStorage.getItem(BREWKEY);
|
||||
if(currentNew && !confirm(
|
||||
`You have some text in the new brew space, if you load a file that text will be lost, are you sure you want to load the file?`
|
||||
)) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e)=>{
|
||||
const fileContent = e.target.result;
|
||||
const newBrew = { text: fileContent, style: '' };
|
||||
|
||||
if(fileContent.startsWith('```metadata')) {
|
||||
splitTextStyleAndMetadata(newBrew);
|
||||
localStorage.setItem(BREWKEY, newBrew.text);
|
||||
localStorage.setItem(STYLEKEY, newBrew.style);
|
||||
localStorage.setItem(METAKEY, JSON.stringify(
|
||||
_.pick(newBrew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])
|
||||
));
|
||||
window.location.href = '/new';
|
||||
return;
|
||||
}
|
||||
|
||||
const type = file.name.split('.').pop().toLowerCase();
|
||||
|
||||
alert(`This file is invalid: ${!type ? "Missing file extension" :`.${type} files are not supported`}. Only .txt files exported from the Homebrewery are allowed.`);
|
||||
|
||||
|
||||
console.log(file);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Nav.dropdown>
|
||||
<Nav.item
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
&::before {
|
||||
margin-right : 5px;
|
||||
font-family : 'Font Awesome 6 Free';
|
||||
font-weight : 900;
|
||||
content : '\f00c';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const Account = require('../../navbar/account.navitem.jsx');
|
||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
||||
const VaultNavItem = require('../../navbar/vault.navitem.jsx');
|
||||
|
||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||
const Editor = require('../../editor/editor.jsx');
|
||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const AccountNavItem = require('../../navbar/account.navitem.jsx');
|
||||
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||
const { fetchThemeBundle } = require('../../../../shared/helpers.js');
|
||||
|
||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||
const Editor = require('../../editor/editor.jsx');
|
||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
||||
|
||||
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
|
||||
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||
const Editor = require('../../editor/editor.jsx');
|
||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ const Account = require('../../navbar/account.navitem.jsx');
|
||||
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
||||
const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
|
||||
const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx');
|
||||
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
|
||||
|
||||
import request from '../../utils/request-middleware.js';
|
||||
|
||||
74
client/homebrew/utils/request-middleware.spec.js
Normal file
74
client/homebrew/utils/request-middleware.spec.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import requestMiddleware from './request-middleware';
|
||||
|
||||
jest.mock('superagent');
|
||||
import request from 'superagent';
|
||||
|
||||
describe('request-middleware', ()=>{
|
||||
let version;
|
||||
|
||||
let setFn;
|
||||
let testFn;
|
||||
|
||||
beforeEach(()=>{
|
||||
jest.resetAllMocks();
|
||||
version = global.version;
|
||||
|
||||
global.version = '999';
|
||||
|
||||
setFn = jest.fn();
|
||||
testFn = jest.fn(()=>{ return { set: setFn }; });
|
||||
});
|
||||
|
||||
afterEach(()=>{
|
||||
global.version = version;
|
||||
});
|
||||
|
||||
it('should add header to get', ()=>{
|
||||
// Ensure tests functions have been reset
|
||||
expect(testFn).not.toHaveBeenCalled();
|
||||
expect(setFn).not.toHaveBeenCalled();
|
||||
|
||||
request.get = testFn;
|
||||
|
||||
requestMiddleware.get('path');
|
||||
|
||||
expect(testFn).toHaveBeenCalledWith('path');
|
||||
expect(setFn).toHaveBeenCalledWith('Homebrewery-Version', '999');
|
||||
});
|
||||
|
||||
it('should add header to put', ()=>{
|
||||
expect(testFn).not.toHaveBeenCalled();
|
||||
expect(setFn).not.toHaveBeenCalled();
|
||||
|
||||
request.put = testFn;
|
||||
|
||||
requestMiddleware.put('path');
|
||||
|
||||
expect(testFn).toHaveBeenCalledWith('path');
|
||||
expect(setFn).toHaveBeenCalledWith('Homebrewery-Version', '999');
|
||||
});
|
||||
|
||||
it('should add header to post', ()=>{
|
||||
expect(testFn).not.toHaveBeenCalled();
|
||||
expect(setFn).not.toHaveBeenCalled();
|
||||
|
||||
request.post = testFn;
|
||||
|
||||
requestMiddleware.post('path');
|
||||
|
||||
expect(testFn).toHaveBeenCalledWith('path');
|
||||
expect(setFn).toHaveBeenCalledWith('Homebrewery-Version', '999');
|
||||
});
|
||||
|
||||
it('should add header to delete', ()=>{
|
||||
expect(testFn).not.toHaveBeenCalled();
|
||||
expect(setFn).not.toHaveBeenCalled();
|
||||
|
||||
request.delete = testFn;
|
||||
|
||||
requestMiddleware.delete('path');
|
||||
|
||||
expect(testFn).toHaveBeenCalledWith('path');
|
||||
expect(setFn).toHaveBeenCalledWith('Homebrewery-Version', '999');
|
||||
});
|
||||
});
|
||||
1364
package-lock.json
generated
1364
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -136,19 +136,19 @@
|
||||
"written-number": "^0.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@stylistic/stylelint-plugin": "^3.1.3",
|
||||
"@stylistic/stylelint-plugin": "^4.0.0",
|
||||
"babel-plugin-transform-import-meta": "^2.3.3",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^16.3.0",
|
||||
"jest": "^30.0.4",
|
||||
"jest": "^30.0.5",
|
||||
"jest-expect-message": "^1.1.3",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"postcss-less": "^6.0.0",
|
||||
"stylelint": "^16.21.1",
|
||||
"stylelint-config-recess-order": "^7.1.0",
|
||||
"stylelint-config-recommended": "^16.0.0",
|
||||
"supertest": "^7.1.3"
|
||||
"stylelint": "^16.23.1",
|
||||
"stylelint-config-recess-order": "^7.2.0",
|
||||
"stylelint-config-recommended": "^17.0.0",
|
||||
"supertest": "^7.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,8 +487,8 @@ app.get('/account', asyncHandler(async (req, res, next)=>{
|
||||
const query = { authors: req.account.username, googleId: { $exists: false } };
|
||||
const mongoCount = await HomebrewModel.countDocuments(query)
|
||||
.catch((err)=>{
|
||||
mongoCount = 0;
|
||||
console.log(err);
|
||||
return 0;
|
||||
});
|
||||
|
||||
data.accountDetails = {
|
||||
|
||||
@@ -52,13 +52,13 @@ const api = {
|
||||
// ID Validation Checks
|
||||
// Homebrewery ID
|
||||
// Typically 12 characters, but the DB shows a range of 7 to 14 characters
|
||||
if(!id.match(/^[A-Za-z0-9_-]{7,14}$/)){
|
||||
if(!id.match(/^[a-zA-Z0-9-_]{7,14}$/)){
|
||||
throw { name: 'ID Error', message: 'Invalid ID', status: 404, HBErrorCode: '11', brewId: id };
|
||||
}
|
||||
// Google ID
|
||||
// Typically 33 characters, old format is 44 - always starts with a 1
|
||||
// Managed by Google, may change outside of our control, so any length between 33 and 44 is acceptable
|
||||
if(googleId && !googleId.match(/^1(?:[A-Za-z0-9+\/]{32,43})$/)){
|
||||
if(googleId && !googleId.match(/^1(?:[a-zA-Z0-9-_]{32,43})$/)){
|
||||
throw { name: 'Google ID Error', message: 'Invalid ID', status: 404, HBErrorCode: '12', brewId: id };
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ const api = {
|
||||
} catch (err) {
|
||||
//debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`);
|
||||
console.error('Failed to apply patches:', {
|
||||
patches : brewFromClient.patches,
|
||||
//patches : brewFromClient.patches,
|
||||
brewId : brewFromClient.editId || 'unknown',
|
||||
error : err
|
||||
});
|
||||
@@ -537,4 +537,4 @@ router.delete('/api/:id', checkClientVersion, asyncHandler(api.deleteBrew));
|
||||
router.get('/api/remove/:id', checkClientVersion, asyncHandler(api.deleteBrew));
|
||||
router.get('/api/theme/:renderer/:id', asyncHandler(api.getThemeBundle));
|
||||
|
||||
export default api;
|
||||
export default api;
|
||||
|
||||
@@ -185,7 +185,7 @@ const mustacheSpans = {
|
||||
start(src) { return src.match(/{{[^{]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const completeSpan = /^{{[^\n]*}}/; // Regex for the complete token
|
||||
const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
|
||||
const inlineRegex = /{{(?=((?:[:=](?:"['\w,\-()#%=?. \&\:\!\@\$\^\*\<\>\;\:\[\]\{\}\-\_\+\=]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1 *|}}/g;
|
||||
const match = completeSpan.exec(src);
|
||||
if(match) {
|
||||
//Find closing delimiter
|
||||
@@ -241,8 +241,8 @@ const mustacheDivs = {
|
||||
level : 'block',
|
||||
start(src) { return src.match(/\n *{{[^{]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const completeBlock = /^ *{{[^\n}]* *\n.*\n *}}/s; // Regex for the complete token
|
||||
const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
|
||||
const completeBlock = /^ *{{[^\n]* *\n.*\n *}}/s; // Regex for the complete token
|
||||
const blockRegex = /^ *{{(?=((?:[:=](?:"['\w,\-()#%=?.\&\:\!\@\$\^\*\<\>\;\:\[\]\{\}\-\_\+\= ]*"|[\w\-()#%. ]*)|[^"=':{}\s]*)*))\1 *$|^ *}}$/gm;
|
||||
const match = completeBlock.exec(src);
|
||||
if(match) {
|
||||
//Find closing delimiter
|
||||
@@ -297,7 +297,7 @@ const mustacheInjectInline = {
|
||||
level : 'inline',
|
||||
start(src) { return src.match(/ *{[^{\n]/)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%=?.\&\:\!\@\$\^\*\<\>\;\:\[\]\{\}\-\_\+\= ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/g;
|
||||
const match = inlineRegex.exec(src);
|
||||
if(match) {
|
||||
const lastToken = tokens[tokens.length - 1];
|
||||
@@ -343,7 +343,7 @@ const mustacheInjectBlock = {
|
||||
level : 'block',
|
||||
start(src) { return src.match(/\n *{[^{\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||
tokenizer(src, tokens) {
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-+*/()#%=?. ]*"|[\w\-+*/()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
||||
const inlineRegex = /^ *{(?=((?:[:=](?:"['\w,\-()#%=?.& ]*"|[\w\-()#%.]*)|[^"=':{}\s]*)*))\1}/ym;
|
||||
const match = inlineRegex.exec(src);
|
||||
if(match) {
|
||||
const lastToken = tokens[tokens.length - 1];
|
||||
@@ -735,6 +735,17 @@ const voidTags = new Set([
|
||||
'input', 'keygen', 'link', 'meta', 'param', 'source'
|
||||
]);
|
||||
|
||||
const notInside = (string, stringMatch1, stringMatch2)=> {
|
||||
const pos1 = string.indexOf(stringMatch1);
|
||||
const pos2 = string.indexOf(stringMatch2);
|
||||
|
||||
if(((pos1 > 0) && (pos2 == -1)) ||
|
||||
((pos1 > 0) && (pos2 > 0) && (pos2 > pos1))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const processStyleTags = (string)=>{
|
||||
//split tags up. quotes can only occur right after : or =.
|
||||
//TODO: can we simplify to just split on commas?
|
||||
@@ -742,7 +753,7 @@ const processStyleTags = (string)=>{
|
||||
|
||||
const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0] || null;
|
||||
const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('='))).join(' ') || null;
|
||||
const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'))
|
||||
const attributes = _.remove(tags, (tag)=>(notInside(tag, '=', ':'))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"'))
|
||||
?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
|
||||
.reduce((obj, attr)=>{
|
||||
const index = attr.indexOf('=');
|
||||
|
||||
Reference in New Issue
Block a user