0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-24 20:43:03 +00:00

Compare commits

..

15 Commits

Author SHA1 Message Date
Trevor Buckner
2bbff081ed Merge branch 'master' into delete-route-for-account-deletion 2025-07-15 16:05:34 -04:00
Víctor Losada Hernández
c3281038bd Merge branch 'master' of https://github.com/naturalcrit/homebrewery into delete-route-for-account-deletion 2025-05-25 15:31:49 +02:00
Víctor Losada Hernández
cc7e2ebc40 Merge branch 'delete-route-for-account-deletion' of https://github.com/naturalcrit/homebrewery into delete-route-for-account-deletion 2025-05-09 18:23:12 +02:00
Víctor Losada Hernández
22be3916b3 remove logs that break tests 2025-05-09 18:23:10 +02:00
Víctor Losada Hernández
49831f4a17 Merge branch 'master' into delete-route-for-account-deletion 2025-05-09 18:16:38 +02:00
Víctor Losada Hernández
19d6ef23a5 Merge branch 'master' into delete-route-for-account-deletion 2025-05-09 12:24:57 +02:00
Víctor Losada Hernández
3da6822017 removing logs 2025-04-19 20:48:23 +02:00
Víctor Losada Hernández
cd676d2509 remove test, fix issues 2025-04-19 20:43:39 +02:00
Víctor Losada Hernández
7c100e608d testing 2025-04-19 20:30:47 +02:00
Víctor Losada Hernández
a1b8a85583 Merge branch 'master' of https://github.com/naturalcrit/homebrewery into delete-route-for-account-deletion 2025-04-19 19:20:36 +02:00
Trevor Buckner
d253023134 Merge branch 'master' into delete-route-for-account-deletion 2025-04-14 14:27:32 -04:00
Víctor Losada Hernández
abee6b98c0 comment for myself 2025-01-11 23:46:15 +01:00
Víctor Losada Hernández
e0d4eafe17 fix deletion 2025-01-11 23:03:26 +01:00
Víctor Losada Hernández
2d3ebfd6bf Merge branch 'master' of https://github.com/naturalcrit/homebrewery into delete-route-for-account-deletion 2025-01-10 11:39:46 +01:00
Víctor Losada Hernández
2d2f1dc84c This initial commit should work 2024-12-26 21:39:35 +01:00
12 changed files with 541 additions and 771 deletions

View File

@@ -1,37 +0,0 @@
require('./editPage.less');
const React = require('react');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../../navbar/navbar.jsx');
const NewBrewItem = require('../../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../../navbar/help.navitem.jsx');
const PrintNavItem = require('../../../navbar/print.navitem.jsx');
const ErrorNavItem = require('../../../navbar/error-navitem.jsx');
const AccountNavItem = require('../../../navbar/account.navitem.jsx');
const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both;
const VaultNavItem = require('../../../navbar/vault.navitem.jsx');
const BaseEditPage = (props)=>{
return (
<div className={`sitePage ${props.className || ''}`}>
<Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{props.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
{props.navButtons}
<PrintNavItem />
<NewBrewItem />
<HelpNavItem />
<VaultNavItem />
<RecentNavItem brew={props.brew} storageKey={props.recentStorageKey} />
<AccountNavItem />
</Nav.section>
</Navbar>
{props.children}
</div>
);
};
module.exports = BaseEditPage;

View File

@@ -11,11 +11,17 @@ import request from '../../utils/request-middleware.js';
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../navbar/help.navitem.jsx');
const PrintNavItem = require('../../navbar/print.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const VaultNavItem = require('../../navbar/vault.navitem.jsx');
const BaseEditPage = require('../basePages/editPage/editPage.jsx');
const SplitPane = require('client/components/splitPane/splitPane.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -260,7 +266,7 @@ const EditPage = createClass({
brew.text = brew.text.normalize('NFC');
this.savedBrew.text = this.savedBrew.text.normalize('NFC');
brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
brew.patches = stringifyPatches(makePatches(encodeURI(this.savedBrew.text), encodeURI(brew.text)));
brew.patches = stringifyPatches(makePatches(this.savedBrew.text, brew.text));
brew.hash = await md5(this.savedBrew.text);
//brew.text = undefined; - Temporary parallel path
brew.textBin = undefined;
@@ -435,7 +441,11 @@ const EditPage = createClass({
renderNavbar : function(){
const shareLink = this.processShareId();
return <>
return <Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
{this.renderGoogleDriveIcon()}
{this.state.error ?
@@ -445,6 +455,8 @@ const EditPage = createClass({
{this.renderAutoSaveButton()}
</Nav.dropdown>
}
<NewBrew />
<HelpNavItem/>
<Nav.dropdown>
<Nav.item color='teal' icon='fas fa-share-alt'>
share
@@ -459,19 +471,20 @@ const EditPage = createClass({
post to reddit
</Nav.item>
</Nav.dropdown>
<PrintNavItem />
<VaultNavItem />
<RecentNavItem brew={this.state.brew} storageKey='edit' />
<Account />
</Nav.section>
</>;
</Navbar>;
},
render : function(){
return <BaseEditPage
className="editPage"
errorState={this.state.error}
parent={this}
brew={this.state.brew}
navButtons={this.renderNavbar()}
recentStorageKey='edit'>
return <div className='editPage sitePage'>
<Meta name='robots' content='noindex, nofollow' />
{this.renderNavbar()}
{this.props.brew.lock && <LockNotification shareId={this.props.brew.shareId} message={this.props.brew.lock.editMessage} reviewRequested={this.props.brew.lock.reviewRequested} />}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove}>
@@ -509,7 +522,7 @@ const EditPage = createClass({
/>
</SplitPane>
</div>
</BaseEditPage>;
</div>;
}
});

View File

@@ -6,11 +6,16 @@ import request from '../../utils/request-middleware.js';
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const NewBrewItem = require('../../navbar/newbrew.navitem.jsx');
const HelpNavItem = require('../../navbar/help.navitem.jsx');
const VaultNavItem = require('../../navbar/vault.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
const { fetchThemeBundle } = require('../../../../shared/helpers.js');
const BaseEditPage = require('../basePages/editPage/editPage.jsx');
const SplitPane = require('client/components/splitPane/splitPane.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -20,13 +25,13 @@ const HomePage = createClass({
displayName : 'HomePage',
getDefaultProps : function() {
return {
brew : DEFAULT_BREW
brew : DEFAULT_BREW,
ver : '0.0.0'
};
},
getInitialState : function() {
return {
brew : this.props.brew,
isSaving : false,
welcomeText : this.props.brew.text,
error : undefined,
currentEditorViewPageNum : 1,
@@ -42,11 +47,7 @@ const HomePage = createClass({
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
},
save : function(){
this.setState({
isSaving : true
});
handleSave : function(){
request.post('/api')
.send(this.state.brew)
.end((err, res)=>{
@@ -56,9 +57,6 @@ const HomePage = createClass({
}
const brew = res.body;
window.location = `/edit/${brew.editId}`;
})
.catch((err)=>{
this.setState({ isSaving: false, error: err });
});
},
handleSplitMove : function(){
@@ -82,38 +80,26 @@ const HomePage = createClass({
brew : { ...prevState.brew, text: text },
}));
},
renderSaveButton : function(){
if(this.state.isSaving){
return <Nav.item icon='fas fa-spinner fa-spin' className='save'>
save...
</Nav.item>;
} else {
return <Nav.item icon='fas fa-save' className='save' onClick={this.save}>
save
</Nav.item>;
}
},
renderNavbar : function(){
return <>
return <Navbar ver={this.props.ver}>
<Nav.section>
{this.state.error ?
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
null
}
<NewBrewItem />
<HelpNavItem />
<VaultNavItem />
<RecentNavItem />
<AccountNavItem />
</Nav.section>
</>;
</Navbar>;
},
render : function(){
return <BaseEditPage
className="homePage"
errorState={this.state.error}
parent={this}
brew={this.state.brew}
navButtons={this.renderNavbar()}>
return <div className='homePage sitePage'>
<Meta name='google-site-verification' content='NwnAQSSJZzAT7N-p5MY6ydQ7Njm67dtbu73ZSyE5Fy4' />
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove}>
<Editor
@@ -141,14 +127,14 @@ const HomePage = createClass({
/>
</SplitPane>
</div>
<div className={cx('floatingSaveButton', { show: this.state.welcomeText != this.state.brew.text })} onClick={this.save}>
<div className={cx('floatingSaveButton', { show: this.state.welcomeText != this.state.brew.text })} onClick={this.handleSave}>
Save current <i className='fas fa-save' />
</div>
<a href='/new' className='floatingNewButton'>
Create your own <i className='fas fa-magic' />
</a>
</BaseEditPage>
</div>;
}
});

View File

@@ -7,10 +7,14 @@ import request from '../../utils/request-middleware.js';
import Markdown from 'naturalcrit/markdown.js';
const Nav = require('naturalcrit/nav/nav.jsx');
const PrintNavItem = require('../../navbar/print.navitem.jsx');
const Navbar = require('../../navbar/navbar.jsx');
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const HelpNavItem = require('../../navbar/help.navitem.jsx');
const BaseEditPage = require('../basePages/editPage/editPage.jsx');
const SplitPane = require('client/components/splitPane/splitPane.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -208,23 +212,28 @@ const NewPage = createClass({
},
renderNavbar : function(){
return <>
return <Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section>
<Nav.section>
{this.state.error ?
<ErrorNavItem error={this.state.error} parent={this}></ErrorNavItem> :
this.renderSaveButton()
}
<PrintNavItem />
<HelpNavItem />
<RecentNavItem />
<AccountNavItem />
</Nav.section>
</>;
</Navbar>;
},
render : function(){
return <BaseEditPage
className="newPage"
errorState={this.state.error}
parent={this}
brew={this.state.brew}
navButtons={this.renderNavbar()}>
return <div className='newPage sitePage'>
{this.renderNavbar()}
<div className='content'>
<SplitPane onDragFinish={this.handleSplitMove}>
<Editor
@@ -259,7 +268,7 @@ const NewPage = createClass({
/>
</SplitPane>
</div>
</BaseEditPage>;
</div>;
}
});

View File

@@ -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('client/components/splitPane/splitPane.jsx');
const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx');
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
import request from '../../utils/request-middleware.js';

1101
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -142,13 +142,13 @@
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-react": "^7.37.5",
"globals": "^16.3.0",
"jest": "^30.0.5",
"jest": "^30.0.4",
"jest-expect-message": "^1.1.3",
"jsdom-global": "^3.0.2",
"postcss-less": "^6.0.0",
"stylelint": "^16.22.0",
"stylelint": "^16.21.1",
"stylelint-config-recess-order": "^7.1.0",
"stylelint-config-recommended": "^16.0.0",
"supertest": "^7.1.4"
"supertest": "^7.1.3"
}
}

View File

@@ -36,7 +36,6 @@ import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import forceSSL from './forcessl.mw.js';
const sanitizeBrew = (brew, accessType)=>{
brew._id = undefined;
brew.__v = undefined;
@@ -373,6 +372,34 @@ app.put('/api/user/rename', async (req, res)=>{
}
});
//Delete brews based on author
app.delete('/api/user/delete', async (req, res)=>{
const { username } = req.body;
const ownAccount = req.account && (req.account.username == username);
if(!ownAccount) return res.status(403).json({ error: 'Must be logged in to delete your account' });
try {
const brews = await HomebrewModel.getByUser(username, true, ['_id', 'googleId', 'editId', 'authors']);
const deletePromises = brews.map((brew)=>{
req.brew = brew;
return new Promise((resolve, reject)=>{
api.deleteBrew(req, res, (err)=>err ? reject(err) : resolve());
});
});
await Promise.all(deletePromises);
return res.json({ success: true, message: `All brews for ${username} have been deleted.` });
} catch (error) {
console.error('Error deleting brews:', error);
if(!res.headersSent) {
return res.status(500).json({ error: 'Failed to delete the brews.' });
}
}
});
//Edit Page
app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{
req.brew = req.brew.toObject ? req.brew.toObject() : req.brew;
@@ -383,7 +410,6 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res,
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
image : req.brew.thumbnail || defaultMetaTags.image,
locale : req.brew.lang,
type : 'article'
};

View File

@@ -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 };
}
@@ -375,14 +375,14 @@ const api = {
try {
const patches = parsePatch(brewFromClient.patches);
// Patch to a throwaway variable while parallelizing - we're more concerned with error/no error.
const patchedResult = decodeURI(applyPatches(patches, encodeURI(brewFromServer.text))[0]);
const patchedResult = applyPatches(patches, brewFromServer.text, { allowExceedingIndices: true })[0];
if(patchedResult != brewFromClient.text)
throw("Patches did not apply cleanly, text mismatch detected");
// brew.text = applyPatches(patches, brewFromServer.text)[0];
} 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;