From fa8d47400faa0675cafb88541b589585ab93b39c Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Wed, 30 Mar 2022 23:23:45 -0500 Subject: [PATCH 01/14] update API and frontend for google drive document stubs --- .../basePages/listPage/brewItem/brewItem.jsx | 14 +- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/newPage/newPage.jsx | 2 +- server/app.js | 134 +++----- server/googleActions.js | 60 +--- server/homebrew.api.js | 319 +++++++++++------- server/homebrew.model.js | 1 + 7 files changed, 266 insertions(+), 266 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index f76d14ebe..b1d30f6f3 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -16,8 +16,8 @@ const BrewItem = createClass({ brew : { title : '', description : '', - - authors : [] + authors : [], + stubbed : true } }; }, @@ -50,7 +50,7 @@ const BrewItem = createClass({ if(!this.props.brew.editId) return; let editLink = this.props.brew.editId; - if(this.props.brew.googleId) { + if(this.props.brew.googleId && !this.props.brew.stubbed) { editLink = this.props.brew.googleId + editLink; } @@ -63,7 +63,7 @@ const BrewItem = createClass({ if(!this.props.brew.shareId) return; let shareLink = this.props.brew.shareId; - if(this.props.brew.googleId) { + if(this.props.brew.googleId && !this.props.brew.stubbed) { shareLink = this.props.brew.googleId + shareLink; } @@ -86,7 +86,7 @@ const BrewItem = createClass({ }, renderGoogleDriveIcon : function(){ - if(!this.props.brew.gDrive) return; + if(!this.props.brew.googleId) return; return googleDriveIcon @@ -104,8 +104,8 @@ const BrewItem = createClass({
- - {brew.authors.join(', ')} + + {brew.authors?.join(', ')}
diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index fa985b7bf..969b80f49 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -211,7 +211,7 @@ const EditPage = createClass({ this.savedBrew = res.body; if(transfer) { - history.replaceState(null, null, `/edit/${this.savedBrew.googleId ?? ''}${this.savedBrew.editId}`); + history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); } this.setState((prevState)=>({ diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index b57da013a..b99d6f85a 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -174,7 +174,7 @@ const NewPage = createClass({ localStorage.removeItem(BREWKEY); localStorage.removeItem(STYLEKEY); localStorage.removeItem(METAKEY); - window.location = `/edit/${brew.googleId ?? ''}${brew.editId}`; + window.location = `/edit/${brew.editId}`; }, renderSaveButton : function(){ diff --git a/server/app.js b/server/app.js index de51c8882..79cda797b 100644 --- a/server/app.js +++ b/server/app.js @@ -9,48 +9,14 @@ const yaml = require('js-yaml'); const app = express(); const config = require('./config.js'); -const homebrewApi = require('./homebrew.api.js'); +const { homebrewApi, getBrew } = require('./homebrew.api.js'); const GoogleActions = require('./googleActions.js'); const serveCompressedStaticAssets = require('./static-assets.mv.js'); const sanitizeFilename = require('sanitize-filename'); const asyncHandler = require('express-async-handler'); -const brewAccessTypes = ['edit', 'share', 'raw']; - -//Get the brew object from the HB database or Google Drive -const getBrewFromId = asyncHandler(async (id, accessType)=>{ - if(!brewAccessTypes.includes(accessType)) - throw ('Invalid Access Type when getting brew'); - let brew; - if(id.length > 12) { - const googleId = id.slice(0, -12); - id = id.slice(-12); - brew = await GoogleActions.getGoogleBrew(googleId, id, accessType); - } else { - brew = await HomebrewModel.get(accessType == 'edit' ? { editId: id } : { shareId: id }); - brew = brew.toObject(); // Convert MongoDB object to standard Javascript Object - } - - brew = sanitizeBrew(brew, accessType === 'edit' ? false : true); - //Split brew.text into text and style - //unless the Access Type is RAW, in which case return immediately - if(accessType == 'raw') { - return brew; - } - splitTextStyleAndMetadata(brew); - return brew; -}); - -const sanitizeBrew = (brew, full=false)=>{ - delete brew._id; - delete brew.__v; - if(full){ - delete brew.editId; - } - return brew; -}; - -const splitTextStyleAndMetadata = (brew)=>{ +const splitTextStyleAndMetadata = (req, res, next)=>{ + const { brew } = req; brew.text = brew.text.replaceAll('\r\n', '\n'); if(brew.text.startsWith('```metadata')) { const index = brew.text.indexOf('```\n\n'); @@ -64,6 +30,24 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.style = brew.text.slice(7, index - 1); brew.text = brew.text.slice(index + 5); } + req.brew = brew; + return next(); +}; + +const sanitizeBrew = (brew, full = false)=>{ + delete brew._id; + delete brew.__v; + if(full){ + delete brew.editId; + } + return brew; +}; + +const sanitizeMiddleware = (accessType)=>{ + return (req, res, next)=>{ + req.brew = sanitizeBrew(req.brew, accessType !== 'edit'); + return next(); + }; }; app.use('/', serveCompressedStaticAssets(`build`)); @@ -108,63 +92,54 @@ app.get('/robots.txt', (req, res)=>{ }); //Home page -app.get('/', async (req, res, next)=>{ - const brew = { +app.get('/', (req, res, next)=>{ + req.brew = { text : welcomeText }; - req.brew = brew; return next(); }); //Home page v3 -app.get('/v3_preview', async (req, res, next)=>{ - const brew = { +app.get('/v3_preview', (req, res, next)=>{ + req.brew = { text : welcomeTextV3, renderer : 'V3' }; - splitTextStyleAndMetadata(brew); - req.brew = brew; return next(); -}); +}, splitTextStyleAndMetadata); //Legacy/Other Document -> v3 Migration Guide -app.get('/migrate', async (req, res, next)=>{ - const brew = { +app.get('/migrate', (req, res, next)=>{ + req.brew = { text : migrateText, renderer : 'V3' }; - splitTextStyleAndMetadata(brew); - req.brew = brew; return next(); -}); +}, splitTextStyleAndMetadata); //Changelog page app.get('/changelog', async (req, res, next)=>{ - const brew = { + req.brew = { title : 'Changelog', text : changelogText, renderer : 'V3' }; - splitTextStyleAndMetadata(brew); - req.brew = brew; return next(); -}); +}, splitTextStyleAndMetadata); //FAQ page app.get('/faq', async (req, res, next)=>{ - const brew = { + req.brew = { title : 'FAQ', text : faqText, renderer : 'V3' }; - splitTextStyleAndMetadata(brew); - req.brew = brew; return next(); -}); +}, splitTextStyleAndMetadata); //Source page -app.get('/source/:id', asyncHandler(async (req, res)=>{ - const brew = await getBrewFromId(req.params.id, 'raw'); +app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ + const { brew } = req; const replaceStrings = { '&': '&', '<': '<', '>': '>' }; let text = brew.text; @@ -173,11 +148,11 @@ app.get('/source/:id', asyncHandler(async (req, res)=>{ } text = `
${text}
`; res.status(200).send(text); -})); +}); //Download brew source page -app.get('/download/:id', asyncHandler(async (req, res)=>{ - const brew = await getBrewFromId(req.params.id, 'raw'); +app.get('/download/:id', asyncHandler(getBrew('share')), sanitizeMiddleware('share'), (req, res)=>{ + const { brew } = req; const prefix = 'HB - '; let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); @@ -188,7 +163,7 @@ app.get('/download/:id', asyncHandler(async (req, res)=>{ 'Content-Disposition' : `attachment; filename="${fileName}.txt"` }); res.status(200).send(brew.text); -})); +}); //User Page app.get('/user/:username', async (req, res, next)=>{ @@ -220,44 +195,35 @@ app.get('/user/:username', async (req, res, next)=>{ }); //Edit Page -app.get('/edit/:id', asyncHandler(async (req, res, next)=>{ +app.get('/edit/:id', asyncHandler(getBrew('edit')), sanitizeMiddleware('edit'), splitTextStyleAndMetadata, (req, res, next)=>{ res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. - const brew = await getBrewFromId(req.params.id, 'edit'); - req.brew = brew; return next(); -})); +}); //New Page -app.get('/new/:id', asyncHandler(async (req, res, next)=>{ - const brew = await getBrewFromId(req.params.id, 'share'); - brew.title = `CLONE - ${brew.title}`; - req.brew = brew; +app.get('/new/:id', asyncHandler(getBrew('share')), sanitizeMiddleware('share'), splitTextStyleAndMetadata, (req, res, next)=>{ + req.brew.title = `CLONE - ${brew.title}`; return next(); -})); +}); //Share Page -app.get('/share/:id', asyncHandler(async (req, res, next)=>{ - const brew = await getBrewFromId(req.params.id, 'share'); +app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ + const { brew } = req; - if(req.params.id.length > 12) { + if(req.params.id.length > 12 && !brew._id) { const googleId = req.params.id.slice(0, -12); const shareId = req.params.id.slice(-12); await GoogleActions.increaseView(googleId, shareId, 'share', brew) - .catch((err)=>{next(err);}); + .catch((err)=>{next(err);}); } else { await HomebrewModel.increaseView({ shareId: brew.shareId }); } - req.brew = brew; return next(); -})); +}), sanitizeMiddleware('share'), splitTextStyleAndMetadata); //Print Page -app.get('/print/:id', asyncHandler(async (req, res, next)=>{ - const brew = await getBrewFromId(req.params.id, 'share'); - req.brew = brew; - return next(); -})); +app.get('/print/:id', asyncHandler(getBrew('share')), sanitizeMiddleware('share'), splitTextStyleAndMetadata); //Render the page const templateFn = require('./../client/template.js'); diff --git a/server/googleActions.js b/server/googleActions.js index 3a202cbce..feb0db401 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -141,13 +141,7 @@ const GoogleActions = { name : `${brew.title}.txt`, description : `${brew.description}`, properties : { - title : brew.title, - published : brew.published, - version : brew.version, - renderer : brew.renderer, - tags : brew.tags, - pageCount : brew.pageCount, - systems : brew.systems.join() + title : brew.title } }, media : { @@ -159,10 +153,9 @@ const GoogleActions = { console.log('Error saving to google'); console.error(err); throw (err); - //return res.status(500).send('Error while saving'); }); - return (brew); + return true; }, newGoogleBrew : async (auth, brew)=>{ @@ -180,12 +173,9 @@ const GoogleActions = { 'description' : `${brew.description}`, 'parents' : [folderId], 'properties' : { //AppProperties is not accessible - 'shareId' : brew.shareId || nanoid(12), - 'editId' : brew.editId || nanoid(12), - 'title' : brew.title, - 'views' : '0', - 'pageCount' : brew.pageCount, - 'renderer' : brew.renderer || 'legacy' + 'shareId' : brew.shareId || nanoid(12), + 'editId' : brew.editId || nanoid(12), + 'title' : brew.title } }; @@ -212,26 +202,7 @@ const GoogleActions = { console.error(err); }); - const newHomebrew = { - text : brew.text, - shareId : fileMetadata.properties.shareId, - editId : fileMetadata.properties.editId, - createdAt : new Date(), - updatedAt : new Date(), - gDrive : true, - googleId : obj.data.id, - pageCount : fileMetadata.properties.pageCount, - - title : brew.title, - description : brew.description, - tags : '', - published : brew.published, - renderer : brew.renderer, - authors : [], - systems : [] - }; - - return newHomebrew; + return obj.data.id; }, getGoogleBrew : async (id, accessId, accessType)=>{ @@ -244,7 +215,6 @@ const GoogleActions = { .catch((err)=>{ console.log('Error loading from Google'); throw (err); - return; }); if(obj) { @@ -254,9 +224,7 @@ const GoogleActions = { throw ('Share ID does not match'); } - const serviceDrive = google.drive({ version: 'v3' }); - - const file = await serviceDrive.files.get({ + const file = await drive.files.get({ fileId : id, fields : 'description, properties', alt : 'media' @@ -273,7 +241,7 @@ const GoogleActions = { text : file.data, description : obj.data.description, - tags : obj.data.properties.tags ? obj.data.properties.tags : '', + tags : obj.data.properties.tags ? obj.data.properties.tags : '', systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [], authors : [], published : obj.data.properties.published ? obj.data.properties.published == 'true' : false, @@ -287,7 +255,6 @@ const GoogleActions = { version : parseInt(obj.data.properties.version) || 0, renderer : obj.data.properties.renderer ? obj.data.properties.renderer : 'legacy', - gDrive : true, googleId : id }; @@ -295,14 +262,11 @@ const GoogleActions = { } }, - deleteGoogleBrew : async (auth, id)=>{ + deleteGoogleBrew : async (auth, id, accessId)=>{ const drive = google.drive({ version: 'v3', auth }); - const googleId = id.slice(0, -12); - const accessId = id.slice(-12); - const obj = await drive.files.get({ - fileId : googleId, + fileId : id, fields : 'properties' }) .catch((err)=>{ @@ -311,11 +275,11 @@ const GoogleActions = { }); if(obj && obj.data.properties.editId != accessId) { - throw ('Not authorized to delete this Google brew'); + throw { status: 403, message: 'Not authorized to delete this Google brew' }; } await drive.files.update({ - fileId : googleId, + fileId : id, resource : { trashed: true } }) .catch((err)=>{ diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 6faa69719..fbe9c7aaa 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -5,6 +5,8 @@ const zlib = require('zlib'); const GoogleActions = require('./googleActions.js'); const Markdown = require('../shared/naturalcrit/markdown.js'); const yaml = require('js-yaml'); +const asyncHandler = require('express-async-handler'); +const { nanoid } = require('nanoid'); // const getTopBrews = (cb) => { // HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) { @@ -12,6 +14,51 @@ const yaml = require('js-yaml'); // }); // }; +const findBrew = async (brewId, accessType, googleObjectId)=>{ + let id = brewId; + let googleId = googleObjectId; + if(id.length > 12) { + googleId = id.slice(0, -12); + id = id.slice(-12); + } + let brew = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) + .catch((err)=>{ + if(googleId) { + console.warn(`Unable to find document stub for ${accessType}Id ${id}`); + } else { + console.warn(err); + } + }); + + let googleBrew; + if(googleId || brew?.googleId) { + googleBrew = await GoogleActions.getGoogleBrew(googleId || brew?.googleId, id, accessType) + .catch((err)=>{ + console.warn(err); + }); + if(!brew && !googleBrew) throw 'Brew not found in database or Google Drive'; + brew = brew ? _.merge(brew, googleBrew) : googleBrew; + } else if(!brew) { + throw 'Brew not found in database'; + } + + return brew; +}; + +const getBrew = (accessType)=>{ + return async (req, res, next)=>{ + const { brew } = req; + + if(!brew) { + const id = req.params.id, googleId = req.body?.googleId; + const found = await findBrew(id, accessType, googleId); + req.brew = accessType !== 'edit' && found.toObject ? found.toObject() : found; + } + + !!next ? next() : undefined; + }; +}; + const mergeBrewText = (brew)=>{ let text = brew.text; if(brew.style !== undefined) { @@ -32,13 +79,21 @@ const MAX_TITLE_LENGTH = 100; const getGoodBrewTitle = (text)=>{ const tokens = Markdown.marked.lexer(text); - return (tokens.find((token)=>token.type == 'heading' || token.type == 'paragraph')?.text || 'No Title') + return (tokens.find((token)=>token.type === 'heading' || token.type === 'paragraph')?.text || 'No Title') .slice(0, MAX_TITLE_LENGTH); }; const excludePropsFromUpdate = (brew)=>{ // Remove undesired properties - const propsToExclude = ['views', 'lastViewed']; + const propsToExclude = ['views', 'lastViewed', 'editId', 'shareId', 'googleId']; + for (const prop of propsToExclude) { + delete brew[prop]; + } + return brew; +}; + +const excludeGoogleProps = (brew)=>{ + const propsToExclude = ['views', 'lastViewed', 'pageCount', 'renderer', 'tags', 'systems', 'published', 'version', 'authors']; for (const prop of propsToExclude) { delete brew[prop]; } @@ -54,28 +109,12 @@ const beforeNewSave = (account, brew)=>{ brew.text = mergeBrewText(brew); }; -const newLocalBrew = async (brew)=>{ - const newHomebrew = new HomebrewModel(brew); - // Compress brew text to binary before saving - newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); - // Delete the non-binary text field since it's not needed anymore - newHomebrew.text = undefined; - - let saved = await newHomebrew.save() - .catch((err)=>{ - console.error(err, err.toString(), err.stack); - throw `Error while creating new brew, ${err.toString()}`; - }); - - saved = saved.toObject(); - saved.gDrive = false; - return saved; -}; - const newGoogleBrew = async (account, brew, res)=>{ const oAuth2Client = GoogleActions.authCheck(account, res); - return await GoogleActions.newGoogleBrew(oAuth2Client, brew); + const newBrew = excludeGoogleProps(_.clone(brew)); + + return await GoogleActions.newGoogleBrew(oAuth2Client, newBrew); }; const newBrew = async (req, res)=>{ @@ -88,148 +127,178 @@ const newBrew = async (req, res)=>{ beforeNewSave(req.account, brew); - let saved; + const newHomebrew = new HomebrewModel(brew); + newHomebrew.editId = nanoid(12); + newHomebrew.shareId = nanoid(12); + + let googleId, saved; if(transferToGoogle) { - saved = await newGoogleBrew(req.account, brew, res) + googleId = await newGoogleBrew(req.account, newHomebrew, res) .catch((err)=>{ - res.status(err.status || err.response.status).send(err.message || err); + res.status(err?.status || err?.response?.status || 500).send(err?.message || err); }); + newHomebrew.textBin = undefined; + newHomebrew.text = undefined; } else { - saved = await newLocalBrew(brew) - .catch((err)=>{ - res.status(500).send(err); - }); + // Compress brew text to binary before saving + newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); + // Delete the non-binary text field since it's not needed anymore + newHomebrew.text = undefined; } + if(transferToGoogle && !googleId) { + if(!res.headersSent) { + res.status(500).send('Unable to save document to Google Drive'); + } + return; + } else if(transferToGoogle) { + newHomebrew.googleId = googleId; + } + + saved = await newHomebrew.save() + .catch((err)=>{ + console.error(err, err.toString(), err.stack); + throw `Error while creating new brew, ${err.toString()}`; + }); if(!saved) return; + saved = saved.toObject(); + return res.status(200).send(saved); }; const updateBrew = async (req, res)=>{ - let brew = excludePropsFromUpdate(req.body); + let brew = Object.assign(req.brew, excludePropsFromUpdate(req.body)); + brew.text = mergeBrewText(brew); const { transferToGoogle, transferFromGoogle } = req.query; - let saved; if(brew.googleId && transferFromGoogle) { - beforeNewSave(req.account, brew); - - saved = await newLocalBrew(brew) + const deleted = await deleteGoogleBrew(req.account, brew.googleId, brew.editId, res) .catch((err)=>{ console.error(err); - res.status(500).send(err); + res.status(err?.status || err?.response?.status || 500).send(err.message || err); }); - if(!saved) return; + if(!deleted) { + if(res.headersSent) { + res.status(500).send('Unable to delete brew from Google'); + } + return; + } - await deleteGoogleBrew(req.account, `${brew.googleId}${brew.editId}`, res) - .catch((err)=>{ - console.error(err); - res.status(err.status || err.response.status).send(err.message || err); - }); + delete brew.googleId; } else if(!brew.googleId && transferToGoogle) { - saved = await newGoogleBrew(req.account, brew, res) + brew.googleId = await newGoogleBrew(req.account, excludeGoogleProps(_.clone(brew)), res) .catch((err)=>{ console.error(err); res.status(err.status || err.response.status).send(err.message || err); }); - if(!saved) return; - - await deleteLocalBrew(req.account, brew.editId) - .catch((err)=>{ - console.error(err); - res.status(err.status).send(err.message); - }); + if(!brew.googleId) { + if(res.headersSent) { + res.status(500).send('Unable to save brew to Google'); + } + return; + } } else if(brew.googleId) { - brew.text = mergeBrewText(brew); - - saved = await GoogleActions.updateGoogleBrew(brew) + const updated = await GoogleActions.updateGoogleBrew(excludeGoogleProps(_.clone(brew))) .catch((err)=>{ console.error(err); - res.status(err.response?.status || 500).send(err); + res.status(err?.response?.status || 500).send(err); }); + if(!updated) { + if(res.headersSent) { + res.status(500).send('Unable to save brew to Google'); + } + return; + } + } + + if(brew.googleId) { + brew.textBin = undefined; + brew.text = undefined; } else { - const dbBrew = await HomebrewModel.get({ editId: req.params.id }) - .catch((err)=>{ - console.error(err); - return res.status(500).send('Error while saving'); - }); - - brew = _.merge(dbBrew, brew); - brew.text = mergeBrewText(brew); - // Compress brew text to binary before saving brew.textBin = zlib.deflateRawSync(brew.text); // Delete the non-binary text field since it's not needed anymore brew.text = undefined; - brew.updatedAt = new Date(); - - if(req.account) { - brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); - } - - brew.markModified('authors'); - brew.markModified('systems'); - - saved = await brew.save(); } - if(!saved) return; + brew.updatedAt = new Date(); + + if(req.account) { + brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); + } + + if(!brew.markModified) { + brew = new HomebrewModel(brew); + } + + brew.markModified('authors'); + brew.markModified('systems'); + + const saved = await brew.save() + .catch((err)=>{ + res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database'); + }); + if(!saved) { + if(!res.headersSent) { + res.status(500).send('Unable to save brew to Homebrewery database'); + } + } if(!res.headersSent) return res.status(200).send(saved); }; -const deleteBrew = async (req, res)=>{ - if(req.params.id.length > 12) { - const deleted = await deleteGoogleBrew(req.account, req.params.id, res) - .catch((err)=>{ - res.status(500).send(err); - }); - if(deleted) return res.status(200).send(); - } else { - const deleted = await deleteLocalBrew(req.account, req.params.id) - .catch((err)=>{ - res.status(err.status).send(err.message); - }); - if(deleted) return res.status(200).send(deleted); - return res.status(200).send(); - } -}; - -const deleteLocalBrew = async (account, id)=>{ - const brew = await HomebrewModel.findOne({ editId: id }); - if(!brew) { - throw { status: 404, message: 'Can not find homebrew with that id' }; - } - - if(account) { - // Remove current user as author - brew.authors = _.pull(brew.authors, account.username); - brew.markModified('authors'); - } - - if(brew.authors.length === 0) { - // Delete brew if there are no authors left - await brew.remove() - .catch((err)=>{ - console.error(err); - throw { status: 500, message: 'Error while removing' }; - }); - } else { - // Otherwise, save the brew with updated author list - return await brew.save() - .catch((err)=>{ - throw { status: 500, message: err }; - }); - } -}; - -const deleteGoogleBrew = async (account, id, res)=>{ +const deleteGoogleBrew = async (account, id, editId, res)=>{ const auth = await GoogleActions.authCheck(account, res); - await GoogleActions.deleteGoogleBrew(auth, id); + await GoogleActions.deleteGoogleBrew(auth, id, editId); return true; }; -router.post('/api', newBrew); -router.put('/api/:id', updateBrew); -router.put('/api/update/:id', updateBrew); -router.delete('/api/:id', deleteBrew); -router.get('/api/remove/:id', deleteBrew); +const deleteBrew = async (req, res)=>{ + const { brew, account } = req; + if(brew.googleId) { + const googleDeleted = await deleteGoogleBrew(account, brew.googleId, brew.editId, res) + .catch((err)=>{ + res.status(500).send(err); + }); + if(!googleDeleted) { + if(!res.headersSent) { + res.status(500).send('Unable to delete brew from Google'); + } + return; + } + } -module.exports = router; + if(brew._id) { + if(account) { + // Remove current user as author + brew.authors = _.pull(brew.authors, account.username); + brew.markModified('authors'); + } + + if(brew.authors.length === 0) { + // Delete brew if there are no authors left + await brew.remove() + .catch((err)=>{ + console.error(err); + throw { status: 500, message: 'Error while removing' }; + }); + } else { + // Otherwise, save the brew with updated author list + await brew.save() + .catch((err)=>{ + throw { status: 500, message: err }; + }); + } + } + + return res.status(204).send(); +}; + +router.post('/api', asyncHandler(newBrew)); +router.put('/api/:id', asyncHandler(getBrew('edit')), asyncHandler(updateBrew)); +router.put('/api/update/:id', asyncHandler(getBrew('edit')), asyncHandler(updateBrew)); +router.delete('/api/:id', asyncHandler(getBrew('edit')), asyncHandler(deleteBrew)); +router.get('/api/remove/:id', asyncHandler(getBrew('edit')), asyncHandler(deleteBrew)); + +module.exports = { + homebrewApi : router, + getBrew +}; diff --git a/server/homebrew.model.js b/server/homebrew.model.js index acc78a624..785baabe7 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -6,6 +6,7 @@ const zlib = require('zlib'); const HomebrewSchema = mongoose.Schema({ shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } }, editId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } }, + googleId : { type: String }, title : { type: String, default: '' }, text : { type: String, default: '' }, textBin : { type: Buffer }, From e5021259bbe02a5d26af5e2e7fd100d56293743f Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Wed, 30 Mar 2022 23:34:39 -0500 Subject: [PATCH 02/14] swap usage of delete on possible Mongoose object for setting the value to undefined --- server/homebrew.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index fbe9c7aaa..c192010a5 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -183,7 +183,7 @@ const updateBrew = async (req, res)=>{ return; } - delete brew.googleId; + brew.googleId = undefined; } else if(!brew.googleId && transferToGoogle) { brew.googleId = await newGoogleBrew(req.account, excludeGoogleProps(_.clone(brew)), res) .catch((err)=>{ From 4941dbb5bd19361fa011c998ac24af0e1d8fd8d6 Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Wed, 6 Apr 2022 11:11:11 -0500 Subject: [PATCH 03/14] add exclusions for stub props --- server/googleActions.js | 21 +++++++++++++++++---- server/homebrew.api.js | 22 ++++++++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/server/googleActions.js b/server/googleActions.js index feb0db401..6b62bb246 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -141,7 +141,14 @@ const GoogleActions = { name : `${brew.title}.txt`, description : `${brew.description}`, properties : { - title : brew.title + 'title' : brew.title, + 'shareId' : brew.shareId || nanoid(12), + 'editId' : brew.editId || nanoid(12), + 'views' : brew.views, + 'pageCount' : brew.pageCount, + 'renderer' : brew.renderer || 'legacy', + 'isStubbed' : true, + 'updatedAt' : new Date().toISOString(), } }, media : { @@ -173,9 +180,15 @@ const GoogleActions = { 'description' : `${brew.description}`, 'parents' : [folderId], 'properties' : { //AppProperties is not accessible - 'shareId' : brew.shareId || nanoid(12), - 'editId' : brew.editId || nanoid(12), - 'title' : brew.title + 'shareId' : brew.shareId || nanoid(12), + 'editId' : brew.editId || nanoid(12), + 'title' : brew.title, + 'views' : '0', + 'pageCount' : brew.pageCount, + 'renderer' : brew.renderer || 'legacy', + 'isStubbed' : true, + 'createdAt' : new Date().toISOString(), + 'version' : 1 } }; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index c192010a5..688cb5a58 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -92,12 +92,20 @@ const excludePropsFromUpdate = (brew)=>{ return brew; }; -const excludeGoogleProps = (brew)=>{ - const propsToExclude = ['views', 'lastViewed', 'pageCount', 'renderer', 'tags', 'systems', 'published', 'version', 'authors']; +const excludeStubProps = (brew)=>{ + const propsToExclude = ['text', 'textBin', 'renderer', 'pageCount', 'views', 'version']; for (const prop of propsToExclude) { - delete brew[prop]; + brew[prop] = undefined; } - return brew; +}; + +const excludeGoogleProps = (brew)=>{ + const modified = brew.toObject ? brew.toObject() : brew; + const propsToExclude = ['tags', 'systems', 'published', 'authors', 'owner']; + for (const prop of propsToExclude) { + delete modified[prop]; + } + return modified; }; const beforeNewSave = (account, brew)=>{ @@ -137,8 +145,7 @@ const newBrew = async (req, res)=>{ .catch((err)=>{ res.status(err?.status || err?.response?.status || 500).send(err?.message || err); }); - newHomebrew.textBin = undefined; - newHomebrew.text = undefined; + excludeStubProps(newHomebrew); } else { // Compress brew text to binary before saving newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); @@ -211,8 +218,7 @@ const updateBrew = async (req, res)=>{ } if(brew.googleId) { - brew.textBin = undefined; - brew.text = undefined; + excludeStubProps(brew); } else { // Compress brew text to binary before saving brew.textBin = zlib.deflateRawSync(brew.text); From 39d29abb1949ee364a2efbb391aa09325a572a42 Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Mon, 11 Apr 2022 12:55:19 -0500 Subject: [PATCH 04/14] update app to list stubbed google docs only once --- server/app.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index 79cda797b..3ed0b6fb2 100644 --- a/server/app.js +++ b/server/app.js @@ -181,7 +181,16 @@ app.get('/user/:username', async (req, res, next)=>{ console.error(err); }); - if(googleBrews) { + for (const brew of brews) { + const match = googleBrews.findIndex((b)=>b.editId === brew.editId); + if(match !== -1) { + brew.googleId = googleBrews[match].googleId; + brew.stubbed = true; + googleBrews.splice(match, 1); + } + } + + if(googleBrews && googleBrews.length > 0) { googleBrews = googleBrews.map((brew)=>({ ...brew, authors: [req.account.username] })); brews = _.concat(brews, googleBrews); } From 8a5e9aa1f6c5e54c2ec7941679d8f6393c0cf694 Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Fri, 15 Apr 2022 00:22:51 -0500 Subject: [PATCH 05/14] update app to better handle google brew stubs --- server/app.js | 63 +++++++++++++++++++++---------------- server/googleActions.js | 42 ++++++++++++------------- server/homebrew.api.js | 69 +++++++++++++++++++---------------------- 3 files changed, 87 insertions(+), 87 deletions(-) diff --git a/server/app.js b/server/app.js index 46984c458..385068561 100644 --- a/server/app.js +++ b/server/app.js @@ -15,8 +15,7 @@ const serveCompressedStaticAssets = require('./static-assets.mv.js'); const sanitizeFilename = require('sanitize-filename'); const asyncHandler = require('express-async-handler'); -const splitTextStyleAndMetadata = (req, res, next)=>{ - const { brew } = req; +const splitTextStyleAndMetadata = (brew)=>{ brew.text = brew.text.replaceAll('\r\n', '\n'); if(brew.text.startsWith('```metadata')) { const index = brew.text.indexOf('```\n\n'); @@ -30,26 +29,17 @@ const splitTextStyleAndMetadata = (req, res, next)=>{ brew.style = brew.text.slice(7, index - 1); brew.text = brew.text.slice(index + 5); } - req.brew = brew; - return next(); }; -const sanitizeBrew = (brew, full = false)=>{ - delete brew._id; - delete brew.__v; - if(full){ - delete brew.editId; +const sanitizeBrew = (brew, accessType)=>{ + brew._id = undefined; + brew.__v = undefined; + if(accessType !== 'edit'){ + brew.editId = undefined; } return brew; }; -const sanitizeMiddleware = (accessType)=>{ - return (req, res, next)=>{ - req.brew = sanitizeBrew(req.brew, accessType !== 'edit'); - return next(); - }; -}; - app.use('/', serveCompressedStaticAssets(`build`)); //app.use(express.static(`${__dirname}/build`)); @@ -105,8 +95,9 @@ app.get('/v3_preview', (req, res, next)=>{ text : welcomeTextV3, renderer : 'V3' }; + splitTextStyleAndMetadata(req.brew); return next(); -}, splitTextStyleAndMetadata); +}); //Legacy/Other Document -> v3 Migration Guide app.get('/migrate', (req, res, next)=>{ @@ -114,8 +105,9 @@ app.get('/migrate', (req, res, next)=>{ text : migrateText, renderer : 'V3' }; + splitTextStyleAndMetadata(req.brew); return next(); -}, splitTextStyleAndMetadata); +}); //Changelog page app.get('/changelog', async (req, res, next)=>{ @@ -124,8 +116,9 @@ app.get('/changelog', async (req, res, next)=>{ text : changelogText, renderer : 'V3' }; + splitTextStyleAndMetadata(req.brew); return next(); -}, splitTextStyleAndMetadata); +}); //FAQ page app.get('/faq', async (req, res, next)=>{ @@ -134,8 +127,9 @@ app.get('/faq', async (req, res, next)=>{ text : faqText, renderer : 'V3' }; + splitTextStyleAndMetadata(req.brew); return next(); -}, splitTextStyleAndMetadata); +}); //Source page app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ @@ -151,8 +145,9 @@ app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ }); //Download brew source page -app.get('/download/:id', asyncHandler(getBrew('share')), sanitizeMiddleware('share'), (req, res)=>{ +app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{ const { brew } = req; + sanitizeBrew(brew, 'share'); const prefix = 'HB - '; let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); @@ -186,6 +181,9 @@ app.get('/user/:username', async (req, res, next)=>{ 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; googleBrews.splice(match, 1); } } @@ -197,20 +195,26 @@ app.get('/user/:username', async (req, res, next)=>{ } req.brews = _.map(brews, (brew)=>{ - return sanitizeBrew(brew, !ownAccount); + return sanitizeBrew(brew, ownAccount ? 'edit' : 'share'); }); return next(); }); //Edit Page -app.get('/edit/:id', asyncHandler(getBrew('edit')), sanitizeMiddleware('edit'), splitTextStyleAndMetadata, (req, res, next)=>{ +app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{ + req.brew = req.brew.toObject ? req.brew.toObject() : req.brew; + sanitizeBrew(req.brew, 'edit'); + console.log('edit', req.brew); + splitTextStyleAndMetadata(req.brew); res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. return next(); }); //New Page -app.get('/new/:id', asyncHandler(getBrew('share')), sanitizeMiddleware('share'), splitTextStyleAndMetadata, (req, res, next)=>{ +app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{ + sanitizeBrew(req.brew, 'share'); + splitTextStyleAndMetadata(req.brew); req.brew.title = `CLONE - ${brew.title}`; return next(); }); @@ -227,12 +231,17 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r } else { await HomebrewModel.increaseView({ shareId: brew.shareId }); } - + sanitizeBrew(req.brew, 'share'); + splitTextStyleAndMetadata(req.brew); return next(); -}), sanitizeMiddleware('share'), splitTextStyleAndMetadata); +})); //Print Page -app.get('/print/:id', asyncHandler(getBrew('share')), sanitizeMiddleware('share'), splitTextStyleAndMetadata); +app.get('/print/:id', asyncHandler(getBrew('share')), (req, res, next)=>{ + sanitizeBrew(req.brew, 'share'); + splitTextStyleAndMetadata(req.brew); + next(); +}); const nodeEnv = config.get('node_env'); const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); diff --git a/server/googleActions.js b/server/googleActions.js index b7e490dd4..5d9e43b71 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -142,15 +142,13 @@ const GoogleActions = { name : `${brew.title}.txt`, description : `${brew.description}`, properties : { - 'title' : brew.title, - 'shareId' : brew.shareId || nanoid(12), - 'editId' : brew.editId || nanoid(12), - 'views' : brew.views, - 'pageCount' : brew.pageCount, - 'renderer' : brew.renderer || 'legacy', - 'isStubbed' : true, - 'updatedAt' : new Date().toISOString(), - thumbnail : brew.thumbnail + title : brew.title, + shareId : brew.shareId || nanoid(12), + editId : brew.editId || nanoid(12), + pageCount : brew.pageCount, + renderer : brew.renderer || 'legacy', + isStubbed : true, + thumbnail : brew.thumbnail } }, media : { @@ -178,20 +176,18 @@ const GoogleActions = { const folderId = await GoogleActions.getGoogleFolder(auth); const fileMetadata = { - 'name' : `${brew.title}.txt`, - 'description' : `${brew.description}`, - 'parents' : [folderId], - 'properties' : { //AppProperties is not accessible - 'shareId' : brew.shareId || nanoid(12), - 'editId' : brew.editId || nanoid(12), - 'title' : brew.title, - 'views' : '0', - 'pageCount' : brew.pageCount, - 'renderer' : brew.renderer || 'legacy', - 'isStubbed' : true, - 'createdAt' : new Date().toISOString(), - 'version' : 1, - 'thumbnail' : brew.thumbnail || '' + name : `${brew.title}.txt`, + description : `${brew.description}`, + parents : [folderId], + properties : { //AppProperties is not accessible + shareId : brew.shareId || nanoid(12), + editId : brew.editId || nanoid(12), + title : brew.title, + pageCount : brew.pageCount, + renderer : brew.renderer || 'legacy', + isStubbed : true, + version : 1, + thumbnail : brew.thumbnail || '' } }; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 688cb5a58..da8526fb9 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -14,48 +14,41 @@ const { nanoid } = require('nanoid'); // }); // }; -const findBrew = async (brewId, accessType, googleObjectId)=>{ - let id = brewId; - let googleId = googleObjectId; - if(id.length > 12) { - googleId = id.slice(0, -12); - id = id.slice(-12); - } - let brew = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) - .catch((err)=>{ - if(googleId) { - console.warn(`Unable to find document stub for ${accessType}Id ${id}`); - } else { - console.warn(err); - } - }); - - let googleBrew; - if(googleId || brew?.googleId) { - googleBrew = await GoogleActions.getGoogleBrew(googleId || brew?.googleId, id, accessType) - .catch((err)=>{ - console.warn(err); - }); - if(!brew && !googleBrew) throw 'Brew not found in database or Google Drive'; - brew = brew ? _.merge(brew, googleBrew) : googleBrew; - } else if(!brew) { - throw 'Brew not found in database'; - } - - return brew; -}; - const getBrew = (accessType)=>{ return async (req, res, next)=>{ const { brew } = req; if(!brew) { - const id = req.params.id, googleId = req.body?.googleId; - const found = await findBrew(id, accessType, googleId); + let id = req.params.id, googleId = req.body?.googleId; + + if(id.length > 12) { + googleId = id.slice(0, -12); + id = id.slice(-12); + } + let found = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) + .catch((err)=>{ + if(googleId) { + console.warn(`Unable to find document stub for ${accessType}Id ${id}`); + } else { + console.warn(err); + } + }); + + if(googleId || found?.googleId) { + const googleBrew = await GoogleActions.getGoogleBrew(googleId || found?.googleId, id, accessType) + .catch((err)=>{ + console.warn(err); + }); + if(!found && !googleBrew) throw 'Brew not found in database or Google Drive'; + found = found ? Object.assign(excludeStubProps(found), excludeGoogleProps(googleBrew)) : googleBrew; + } else if(!found) { + throw 'Brew not found in database'; + } + req.brew = accessType !== 'edit' && found.toObject ? found.toObject() : found; } - !!next ? next() : undefined; + next(); }; }; @@ -85,7 +78,7 @@ const getGoodBrewTitle = (text)=>{ const excludePropsFromUpdate = (brew)=>{ // Remove undesired properties - const propsToExclude = ['views', 'lastViewed', 'editId', 'shareId', 'googleId']; + const propsToExclude = ['_id', 'views', 'lastViewed', 'editId', 'shareId', 'googleId']; for (const prop of propsToExclude) { delete brew[prop]; } @@ -93,15 +86,16 @@ const excludePropsFromUpdate = (brew)=>{ }; const excludeStubProps = (brew)=>{ - const propsToExclude = ['text', 'textBin', 'renderer', 'pageCount', 'views', 'version']; + const propsToExclude = ['text', 'textBin', 'renderer', 'pageCount', 'version']; for (const prop of propsToExclude) { brew[prop] = undefined; } + return brew; }; const excludeGoogleProps = (brew)=>{ const modified = brew.toObject ? brew.toObject() : brew; - const propsToExclude = ['tags', 'systems', 'published', 'authors', 'owner']; + const propsToExclude = ['tags', 'systems', 'published', 'authors', 'owner', 'views']; for (const prop of propsToExclude) { delete modified[prop]; } @@ -173,6 +167,7 @@ const newBrew = async (req, res)=>{ }; const updateBrew = async (req, res)=>{ + console.log(req.brew, req.body); let brew = Object.assign(req.brew, excludePropsFromUpdate(req.body)); brew.text = mergeBrewText(brew); const { transferToGoogle, transferFromGoogle } = req.query; From 01441e06104f6321b6152927a158b1c48bdedb9c Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Wed, 11 May 2022 23:34:48 -0500 Subject: [PATCH 06/14] adjust code based on feedback --- server/app.js | 25 ++++++---- server/homebrew.api.js | 107 +++++++++++++++++------------------------ 2 files changed, 60 insertions(+), 72 deletions(-) diff --git a/server/app.js b/server/app.js index eb264bcc9..a5244ca00 100644 --- a/server/app.js +++ b/server/app.js @@ -205,7 +205,6 @@ app.get('/user/:username', async (req, res, next)=>{ app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{ req.brew = req.brew.toObject ? req.brew.toObject() : req.brew; sanitizeBrew(req.brew, 'edit'); - console.log('edit', req.brew); splitTextStyleAndMetadata(req.brew); res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. return next(); @@ -261,7 +260,7 @@ if(isLocalEnvironment){ //Render the page const templateFn = require('./../client/template.js'); -app.use((req, res)=>{ +app.use(asyncHandler(async (req, res, next)=>{ // Create configuration object const configuration = { local : isLocalEnvironment, @@ -279,13 +278,14 @@ app.use((req, res)=>{ config : configuration }; const title = req.brew ? req.brew.title : ''; - templateFn('homebrew', title, props) - .then((page)=>{ res.send(page); }) - .catch((err)=>{ - console.log(err); - return res.sendStatus(500); - }); -}); + const page = await templateFn('homebrew', title, props) + .catch((err)=>{ + console.log(err); + return res.sendStatus(500); + }); + if(!page) return; + res.send(page); +})); //v=====----- Error-Handling Middleware -----=====v// //Format Errors so all fields will be sent @@ -309,6 +309,13 @@ app.use((err, req, res, next)=>{ console.error(err); res.status(status).send(getPureError(err)); }); + +app.use((req, res)=>{ + if(!res.headersSent) { + console.error('Headers have not been sent, responding with a server error.', req.url); + res.status(500).send('An error occurred and the server did not send a response. The error has been logged, please note the time this occurred and report this issue.'); + } +}); //^=====--------------------------------------=====^// module.exports = { diff --git a/server/homebrew.api.js b/server/homebrew.api.js index da8526fb9..0b4b59247 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ const _ = require('lodash'); const HomebrewModel = require('./homebrew.model.js').model; const router = require('express').Router(); @@ -25,7 +26,7 @@ const getBrew = (accessType)=>{ googleId = id.slice(0, -12); id = id.slice(-12); } - let found = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) + let stub = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) .catch((err)=>{ if(googleId) { console.warn(`Unable to find document stub for ${accessType}Id ${id}`); @@ -33,19 +34,24 @@ const getBrew = (accessType)=>{ console.warn(err); } }); + stub = stub?.toObject(); - if(googleId || found?.googleId) { - const googleBrew = await GoogleActions.getGoogleBrew(googleId || found?.googleId, id, accessType) + if(googleId || stub?.googleId) { + let googleError; + const googleBrew = await GoogleActions.getGoogleBrew(googleId || stub?.googleId, id, accessType) .catch((err)=>{ console.warn(err); + googleError = err; }); - if(!found && !googleBrew) throw 'Brew not found in database or Google Drive'; - found = found ? Object.assign(excludeStubProps(found), excludeGoogleProps(googleBrew)) : googleBrew; - } else if(!found) { + if(!googleBrew) throw googleError; + stub = stub ? _.assign(excludeStubProps(stub), excludeGoogleProps(googleBrew)) : googleBrew; + } + + if(!stub) { throw 'Brew not found in database'; } - req.brew = accessType !== 'edit' && found.toObject ? found.toObject() : found; + req.brew = stub; } next(); @@ -78,11 +84,21 @@ const getGoodBrewTitle = (text)=>{ const excludePropsFromUpdate = (brew)=>{ // Remove undesired properties + const modified = _.clone(brew); const propsToExclude = ['_id', 'views', 'lastViewed', 'editId', 'shareId', 'googleId']; for (const prop of propsToExclude) { - delete brew[prop]; + delete modified[prop]; } - return brew; + return modified; +}; + +const excludeGoogleProps = (brew)=>{ + const modified = _.clone(brew); + const propsToExclude = ['tags', 'systems', 'published', 'authors', 'owner', 'views']; + for (const prop of propsToExclude) { + delete modified[prop]; + } + return modified; }; const excludeStubProps = (brew)=>{ @@ -93,15 +109,6 @@ const excludeStubProps = (brew)=>{ return brew; }; -const excludeGoogleProps = (brew)=>{ - const modified = brew.toObject ? brew.toObject() : brew; - const propsToExclude = ['tags', 'systems', 'published', 'authors', 'owner', 'views']; - for (const prop of propsToExclude) { - delete modified[prop]; - } - return modified; -}; - const beforeNewSave = (account, brew)=>{ if(!brew.title) { brew.title = getGoodBrewTitle(brew.text); @@ -114,7 +121,7 @@ const beforeNewSave = (account, brew)=>{ const newGoogleBrew = async (account, brew, res)=>{ const oAuth2Client = GoogleActions.authCheck(account, res); - const newBrew = excludeGoogleProps(_.clone(brew)); + const newBrew = excludeGoogleProps(brew); return await GoogleActions.newGoogleBrew(oAuth2Client, newBrew); }; @@ -137,23 +144,18 @@ const newBrew = async (req, res)=>{ if(transferToGoogle) { googleId = await newGoogleBrew(req.account, newHomebrew, res) .catch((err)=>{ + console.error(err); res.status(err?.status || err?.response?.status || 500).send(err?.message || err); }); + if(!googleId) return; excludeStubProps(newHomebrew); + newHomebrew.googleId = googleId; } else { // Compress brew text to binary before saving newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); // Delete the non-binary text field since it's not needed anymore newHomebrew.text = undefined; } - if(transferToGoogle && !googleId) { - if(!res.headersSent) { - res.status(500).send('Unable to save document to Google Drive'); - } - return; - } else if(transferToGoogle) { - newHomebrew.googleId = googleId; - } saved = await newHomebrew.save() .catch((err)=>{ @@ -163,12 +165,11 @@ const newBrew = async (req, res)=>{ if(!saved) return; saved = saved.toObject(); - return res.status(200).send(saved); + res.status(200).send(saved); }; const updateBrew = async (req, res)=>{ - console.log(req.brew, req.body); - let brew = Object.assign(req.brew, excludePropsFromUpdate(req.body)); + let brew = _.assign(req.brew, excludePropsFromUpdate(req.body)); brew.text = mergeBrewText(brew); const { transferToGoogle, transferFromGoogle } = req.query; @@ -178,38 +179,23 @@ const updateBrew = async (req, res)=>{ console.error(err); res.status(err?.status || err?.response?.status || 500).send(err.message || err); }); - if(!deleted) { - if(res.headersSent) { - res.status(500).send('Unable to delete brew from Google'); - } - return; - } + if(!deleted) return; brew.googleId = undefined; } else if(!brew.googleId && transferToGoogle) { - brew.googleId = await newGoogleBrew(req.account, excludeGoogleProps(_.clone(brew)), res) + brew.googleId = await newGoogleBrew(req.account, excludeGoogleProps(brew), res) .catch((err)=>{ console.error(err); res.status(err.status || err.response.status).send(err.message || err); }); - if(!brew.googleId) { - if(res.headersSent) { - res.status(500).send('Unable to save brew to Google'); - } - return; - } + if(!brew.googleId) return; } else if(brew.googleId) { - const updated = await GoogleActions.updateGoogleBrew(excludeGoogleProps(_.clone(brew))) + const updated = await GoogleActions.updateGoogleBrew(excludeGoogleProps(brew)) .catch((err)=>{ console.error(err); res.status(err?.response?.status || 500).send(err); }); - if(!updated) { - if(res.headersSent) { - res.status(500).send('Unable to save brew to Google'); - } - return; - } + if(!updated) return; } if(brew.googleId) { @@ -226,6 +212,8 @@ const updateBrew = async (req, res)=>{ brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); } + brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); + if(!brew.markModified) { brew = new HomebrewModel(brew); } @@ -235,15 +223,12 @@ const updateBrew = async (req, res)=>{ const saved = await brew.save() .catch((err)=>{ + console.error(err); res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database'); }); - if(!saved) { - if(!res.headersSent) { - res.status(500).send('Unable to save brew to Homebrewery database'); - } - } + if(!saved) return; - if(!res.headersSent) return res.status(200).send(saved); + res.status(200).send(saved); }; const deleteGoogleBrew = async (account, id, editId, res)=>{ @@ -257,14 +242,10 @@ const deleteBrew = async (req, res)=>{ if(brew.googleId) { const googleDeleted = await deleteGoogleBrew(account, brew.googleId, brew.editId, res) .catch((err)=>{ + console.error(err); res.status(500).send(err); }); - if(!googleDeleted) { - if(!res.headersSent) { - res.status(500).send('Unable to delete brew from Google'); - } - return; - } + if(!googleDeleted) return; } if(brew._id) { @@ -290,7 +271,7 @@ const deleteBrew = async (req, res)=>{ } } - return res.status(204).send(); + res.status(204).send(); }; router.post('/api', asyncHandler(newBrew)); From ac905ddf3f05e44dec32e94e8c9d7f03d4bc0622 Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Sat, 14 May 2022 04:17:39 +0000 Subject: [PATCH 07/14] add comments, update query params, adjust code based on feedback --- client/homebrew/pages/editPage/editPage.jsx | 2 +- client/homebrew/pages/newPage/newPage.jsx | 2 +- server/homebrew.api.js | 107 ++++++++++++-------- 3 files changed, 65 insertions(+), 46 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 7de448315..9ef33f165 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -200,7 +200,7 @@ const EditPage = createClass({ const brew = this.state.brew; brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - const params = `${transfer ? `?transfer${this.state.saveGoogle ? 'To' : 'From'}Google=true` : ''}`; + const params = `${transfer ? `?${this.state.saveGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : ''}`; const res = await request .put(`/api/update/${brew.editId}${params}`) .send(brew) diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 657371fb1..ae57039b5 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -162,7 +162,7 @@ const NewPage = createClass({ brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; const res = await request - .post(`/api${this.state.saveGoogle ? '?transferToGoogle=true' : ''}`) + .post(`/api${this.state.saveGoogle ? '?saveToGoogle=true' : ''}`) .send(brew) .catch((err)=>{ console.log(err); diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 0b4b59247..d04e0ed55 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -16,43 +16,47 @@ const { nanoid } = require('nanoid'); // }; const getBrew = (accessType)=>{ + // Create middleware with the accessType passed in as part of the scope return async (req, res, next)=>{ - const { brew } = req; + // Set the id and initial potential google id, where the google id is present on the existing brew. + let id = req.params.id, googleId = req.body?.googleId; - if(!brew) { - let id = req.params.id, googleId = req.body?.googleId; - - if(id.length > 12) { - googleId = id.slice(0, -12); - id = id.slice(-12); - } - let stub = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) - .catch((err)=>{ - if(googleId) { - console.warn(`Unable to find document stub for ${accessType}Id ${id}`); - } else { - console.warn(err); - } - }); - stub = stub?.toObject(); - - if(googleId || stub?.googleId) { - let googleError; - const googleBrew = await GoogleActions.getGoogleBrew(googleId || stub?.googleId, id, accessType) - .catch((err)=>{ - console.warn(err); - googleError = err; - }); - if(!googleBrew) throw googleError; - stub = stub ? _.assign(excludeStubProps(stub), excludeGoogleProps(googleBrew)) : googleBrew; - } - - if(!stub) { - throw 'Brew not found in database'; - } - - req.brew = stub; + // If the id is longer than 12, then it's a google id + the edit id. This splits the longer id up. + if(id.length > 12) { + googleId = id.slice(0, -12); + id = id.slice(-12); } + // Try to find the document in the Homebrewery database -- if it doesn't exist, that's fine. + let stub = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) + .catch((err)=>{ + if(googleId) { + console.warn(`Unable to find document stub for ${accessType}Id ${id}`); + } else { + console.warn(err); + } + }); + stub = stub?.toObject(); + + // If there is a google id, try to find the google brew + if(googleId || stub?.googleId) { + let googleError; + const googleBrew = await GoogleActions.getGoogleBrew(googleId || stub?.googleId, id, accessType) + .catch((err)=>{ + console.warn(err); + googleError = err; + }); + // If we can't find the google brew and there is a google id for the brew, throw an error. + if(!googleBrew) throw googleError; + // Combine the Homebrewery stub with the google brew, or if the stub doesn't exist just use the google brew + stub = stub ? _.assign(excludeStubProps(stub), excludeGoogleProps(googleBrew)) : googleBrew; + } + + // If after all of that we still don't have a brew, throw an exception + if(!stub) { + throw 'Brew not found in Homebrewery database or Google Drive'; + } + + req.brew = stub; next(); }; @@ -169,20 +173,27 @@ const newBrew = async (req, res)=>{ }; const updateBrew = async (req, res)=>{ + // Initialize brew from request and body, destructure query params, set a constant for the google id, and set the initial value for the after-save method let brew = _.assign(req.brew, excludePropsFromUpdate(req.body)); - brew.text = mergeBrewText(brew); - const { transferToGoogle, transferFromGoogle } = req.query; + const { saveToGoogle, removeFromGoogle } = req.query; + const googleId = brew.googleId; + let afterSave = async ()=>true; - if(brew.googleId && transferFromGoogle) { - const deleted = await deleteGoogleBrew(req.account, brew.googleId, brew.editId, res) - .catch((err)=>{ - console.error(err); - res.status(err?.status || err?.response?.status || 500).send(err.message || err); - }); - if(!deleted) return; + brew.text = mergeBrewText(brew); + + if(brew.googleId && removeFromGoogle) { + // If the google id exists and we're removing it from google, set afterSave to delete the google brew and mark the brew's google id as undefined + afterSave = async ()=>{ + return await deleteGoogleBrew(req.account, googleId, brew.editId, res) + .catch((err)=>{ + console.error(err); + res.status(err?.status || err?.response?.status || 500).send(err.message || err); + }); + } brew.googleId = undefined; - } else if(!brew.googleId && transferToGoogle) { + } else if(!brew.googleId && saveToGoogle) { + // If we don't have a google id and the user wants to save to google, create the google brew and set the google id on the brew brew.googleId = await newGoogleBrew(req.account, excludeGoogleProps(brew), res) .catch((err)=>{ console.error(err); @@ -190,6 +201,7 @@ const updateBrew = async (req, res)=>{ }); 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(excludeGoogleProps(brew)) .catch((err)=>{ console.error(err); @@ -199,6 +211,7 @@ const updateBrew = async (req, res)=>{ } if(brew.googleId) { + // If the google id exists after all those actions, exclude the props that are stored in google and aren't needed for rendering the brew items excludeStubProps(brew); } else { // Compress brew text to binary before saving @@ -212,21 +225,27 @@ const updateBrew = async (req, res)=>{ brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); } + // Fetch the brew from the database again (if it existed there to begin with), and assign the existing brew to it brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); if(!brew.markModified) { + // If it wasn't in the database, create a new db brew brew = new HomebrewModel(brew); } brew.markModified('authors'); brew.markModified('systems'); + // Save the database brew const saved = await brew.save() .catch((err)=>{ console.error(err); res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database'); }); if(!saved) return; + // Call and wait for afterSave to complete + const after = await afterSave(); + if (!after) return; res.status(200).send(saved); }; From 74d22a05d0e9e5303efda712ef3934c26df6f232 Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Sat, 14 May 2022 04:19:36 +0000 Subject: [PATCH 08/14] swap transferToGoogle to saveToGoogle in a spot I missed --- server/homebrew.api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index d04e0ed55..6297d75cb 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -132,7 +132,7 @@ const newGoogleBrew = async (account, brew, res)=>{ const newBrew = async (req, res)=>{ const brew = req.body; - const { transferToGoogle } = req.query; + const { saveToGoogle } = req.query; delete brew.editId; delete brew.shareId; @@ -145,7 +145,7 @@ const newBrew = async (req, res)=>{ newHomebrew.shareId = nanoid(12); let googleId, saved; - if(transferToGoogle) { + if(saveToGoogle) { googleId = await newGoogleBrew(req.account, newHomebrew, res) .catch((err)=>{ console.error(err); From 452aa1feaffce95cb10b6e0b741411f1288d1362 Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Sat, 14 May 2022 04:19:54 +0000 Subject: [PATCH 09/14] lint --- server/homebrew.api.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 6297d75cb..e6f304aa4 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -18,7 +18,7 @@ const { nanoid } = require('nanoid'); const getBrew = (accessType)=>{ // Create middleware with the accessType passed in as part of the scope return async (req, res, next)=>{ - // Set the id and initial potential google id, where the google id is present on the existing brew. + // Set the id and initial potential google id, where the google id is present on the existing brew. let id = req.params.id, googleId = req.body?.googleId; // If the id is longer than 12, then it's a google id + the edit id. This splits the longer id up. @@ -26,7 +26,7 @@ const getBrew = (accessType)=>{ googleId = id.slice(0, -12); id = id.slice(-12); } - // Try to find the document in the Homebrewery database -- if it doesn't exist, that's fine. + // Try to find the document in the Homebrewery database -- if it doesn't exist, that's fine. let stub = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) .catch((err)=>{ if(googleId) { @@ -181,7 +181,7 @@ const updateBrew = async (req, res)=>{ brew.text = mergeBrewText(brew); - if(brew.googleId && removeFromGoogle) { + if(brew.googleId && removeFromGoogle) { // If the google id exists and we're removing it from google, set afterSave to delete the google brew and mark the brew's google id as undefined afterSave = async ()=>{ return await deleteGoogleBrew(req.account, googleId, brew.editId, res) @@ -189,7 +189,7 @@ const updateBrew = async (req, res)=>{ console.error(err); res.status(err?.status || err?.response?.status || 500).send(err.message || err); }); - } + }; brew.googleId = undefined; } else if(!brew.googleId && saveToGoogle) { @@ -245,7 +245,7 @@ const updateBrew = async (req, res)=>{ if(!saved) return; // Call and wait for afterSave to complete const after = await afterSave(); - if (!after) return; + if(!after) return; res.status(200).send(saved); }; From 55c91217aba07b530596910bf12b4fb18554fc1d Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Wed, 25 May 2022 03:03:43 +0000 Subject: [PATCH 10/14] fix deletion, update urls --- .../basePages/listPage/brewItem/brewItem.jsx | 2 +- client/homebrew/pages/editPage/editPage.jsx | 6 ++--- client/homebrew/pages/sharePage/sharePage.jsx | 2 +- server/app.js | 2 +- server/homebrew.api.js | 23 +++++++++++++++---- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index 7f614c2c4..5756b0df1 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -76,7 +76,7 @@ const BrewItem = createClass({ if(!this.props.brew.shareId) return; let shareLink = this.props.brew.shareId; - if(this.props.brew.googleId) { + if(this.props.brew.googleId && !this.props.brew.stubbed) { shareLink = this.props.brew.googleId + shareLink; } diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 9ef33f165..f8ac70a5d 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -210,9 +210,7 @@ const EditPage = createClass({ }); this.savedBrew = res.body; - if(transfer) { - history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); - } + history.replaceState(null, null, `/edit/${this.savedBrew.editId}`); this.setState((prevState)=>({ brew : _.merge({}, prevState.brew, { @@ -340,7 +338,7 @@ const EditPage = createClass({ }, processShareId : function() { - return this.state.brew.googleId ? + return this.state.brew.googleId && !this.state.brew.stubbed ? this.state.brew.googleId + this.state.brew.shareId : this.state.brew.shareId; }, diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 372dbbc2c..c983512ad 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -49,7 +49,7 @@ const SharePage = createClass({ }, processShareId : function() { - return this.props.brew.googleId ? + return this.props.brew.googleId && !this.props.brew.stubbed ? this.props.brew.googleId + this.props.brew.shareId : this.props.brew.shareId; }, diff --git a/server/app.js b/server/app.js index 5558de264..74c66508f 100644 --- a/server/app.js +++ b/server/app.js @@ -190,7 +190,7 @@ app.get('/user/:username', async (req, res, next)=>{ }); for (const brew of brews) { - const match = googleBrews.findIndex((b)=>b.editId === brew.editId); + const match = googleBrews?.findIndex((b)=>b.editId === brew.editId) ?? -1; if(match !== -1) { brew.googleId = googleBrews[match].googleId; brew.stubbed = true; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e6f304aa4..460127b1b 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -48,7 +48,7 @@ const getBrew = (accessType)=>{ // If we can't find the google brew and there is a google id for the brew, throw an error. if(!googleBrew) throw googleError; // Combine the Homebrewery stub with the google brew, or if the stub doesn't exist just use the google brew - stub = stub ? _.assign(excludeStubProps(stub), excludeGoogleProps(googleBrew)) : googleBrew; + stub = stub ? _.assign({ ...excludeStubProps(stub), stubbed: true }, excludeGoogleProps(googleBrew)) : googleBrew; } // If after all of that we still don't have a brew, throw an exception @@ -257,17 +257,22 @@ const deleteGoogleBrew = async (account, id, editId, res)=>{ }; const deleteBrew = async (req, res)=>{ - const { brew, account } = req; - if(brew.googleId) { - const googleDeleted = await deleteGoogleBrew(account, brew.googleId, brew.editId, res) + let brew = req.brew; + const { googleId, editId } = brew; + const account = req.account; + const isOwner = account && (brew.authors.length === 0 || brew.authors[0] === account.username); + console.log(brew.authors, googleId, account.username); + let afterSave = async ()=>true; + if(googleId && isOwner) { + afterSave = async ()=>await deleteGoogleBrew(account, googleId, editId, res) .catch((err)=>{ console.error(err); res.status(500).send(err); }); - if(!googleDeleted) return; } if(brew._id) { + brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); if(account) { // Remove current user as author brew.authors = _.pull(brew.authors, account.username); @@ -282,6 +287,12 @@ const deleteBrew = async (req, res)=>{ throw { status: 500, message: 'Error while removing' }; }); } else { + if(googleId && isOwner) { + brew.googleId = undefined; + brew.textBin = zlib.deflateRawSync(brew.text); + brew.text = undefined; + } + // Otherwise, save the brew with updated author list await brew.save() .catch((err)=>{ @@ -289,6 +300,8 @@ const deleteBrew = async (req, res)=>{ }); } } + const after = await afterSave(); + if(!after) return; res.status(204).send(); }; From 627c52e8457622c7ea3be71c322016b08df6effa Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Thu, 26 May 2022 04:40:33 +0000 Subject: [PATCH 11/14] Add comments and adjust google brew for loop --- server/app.js | 24 ++++++++++++------------ server/homebrew.api.js | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/server/app.js b/server/app.js index 74c66508f..b1397ca13 100644 --- a/server/app.js +++ b/server/app.js @@ -189,19 +189,19 @@ app.get('/user/:username', async (req, res, next)=>{ console.error(err); }); - for (const brew of brews) { - const match = googleBrews?.findIndex((b)=>b.editId === brew.editId) ?? -1; - 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; - googleBrews.splice(match, 1); - } - } - if(googleBrews && googleBrews.length > 0) { + for (const brew of brews) { + 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; + googleBrews.splice(match, 1); + } + } + googleBrews = googleBrews.map((brew)=>({ ...brew, authors: [req.account.username] })); brews = _.concat(brews, googleBrews); } diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 460127b1b..a7225beab 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -261,15 +261,8 @@ const deleteBrew = async (req, res)=>{ const { googleId, editId } = brew; const account = req.account; const isOwner = account && (brew.authors.length === 0 || brew.authors[0] === account.username); - console.log(brew.authors, googleId, account.username); - let afterSave = async ()=>true; - if(googleId && isOwner) { - afterSave = async ()=>await deleteGoogleBrew(account, googleId, editId, res) - .catch((err)=>{ - console.error(err); - res.status(500).send(err); - }); - } + // If the user is the owner and the file is saved to google, mark the google brew for deletion + const deleteGoogleBrew = googleId && isOwner; if(brew._id) { brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); @@ -287,7 +280,8 @@ const deleteBrew = async (req, res)=>{ throw { status: 500, message: 'Error while removing' }; }); } else { - if(googleId && isOwner) { + if(deleteGoogleBrew) { + // When there are still authors remaining, we delete the google brew but store the full brew in the Homebrewery database brew.googleId = undefined; brew.textBin = zlib.deflateRawSync(brew.text); brew.text = undefined; @@ -300,8 +294,14 @@ const deleteBrew = async (req, res)=>{ }); } } - const after = await afterSave(); - if(!after) return; + if(deleteGoogleBrew) { + const deleted = await deleteGoogleBrew(account, googleId, editId, res) + .catch((err)=>{ + console.error(err); + res.status(500).send(err); + }); + if(!deleted) return; + } res.status(204).send(); }; From b54b7e317ee1e447afdfe0b9e76e2b72a13f650d Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Thu, 26 May 2022 04:45:50 +0000 Subject: [PATCH 12/14] pre-filter brews list when finding google brews --- server/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index b1397ca13..534ed1e48 100644 --- a/server/app.js +++ b/server/app.js @@ -190,7 +190,7 @@ app.get('/user/:username', async (req, res, next)=>{ }); if(googleBrews && googleBrews.length > 0) { - for (const brew of brews) { + 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; From bade09c24c1f2cafaea48e8b9f3cdb34c38027ad Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Thu, 26 May 2022 04:46:07 +0000 Subject: [PATCH 13/14] linting --- server/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index 534ed1e48..da81dbd42 100644 --- a/server/app.js +++ b/server/app.js @@ -190,7 +190,7 @@ app.get('/user/:username', async (req, res, next)=>{ }); if(googleBrews && googleBrews.length > 0) { - for (const brew of brews.filter(brew => brew.googleId)) { + 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; From 92b3f8252ebfd5e213cabca8c7f0d5076819486d Mon Sep 17 00:00:00 2001 From: Charlie Humphreys Date: Mon, 6 Jun 2022 14:59:40 +0000 Subject: [PATCH 14/14] fix data reuse and add field to projection --- server/app.js | 1 + server/homebrew.api.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/server/app.js b/server/app.js index da81dbd42..b30040d1e 100644 --- a/server/app.js +++ b/server/app.js @@ -165,6 +165,7 @@ app.get('/user/:username', async (req, res, next)=>{ const ownAccount = req.account && (req.account.username == req.params.username); const fields = [ + 'googleId', 'title', 'pageCount', 'description', diff --git a/server/homebrew.api.js b/server/homebrew.api.js index a7225beab..4af232b4c 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -262,7 +262,7 @@ const deleteBrew = async (req, res)=>{ const account = req.account; const isOwner = account && (brew.authors.length === 0 || brew.authors[0] === account.username); // If the user is the owner and the file is saved to google, mark the google brew for deletion - const deleteGoogleBrew = googleId && isOwner; + const shouldDeleteGoogleBrew = googleId && isOwner; if(brew._id) { brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); @@ -280,7 +280,7 @@ const deleteBrew = async (req, res)=>{ throw { status: 500, message: 'Error while removing' }; }); } else { - if(deleteGoogleBrew) { + if(shouldDeleteGoogleBrew) { // When there are still authors remaining, we delete the google brew but store the full brew in the Homebrewery database brew.googleId = undefined; brew.textBin = zlib.deflateRawSync(brew.text); @@ -294,7 +294,7 @@ const deleteBrew = async (req, res)=>{ }); } } - if(deleteGoogleBrew) { + if(shouldDeleteGoogleBrew) { const deleted = await deleteGoogleBrew(account, googleId, editId, res) .catch((err)=>{ console.error(err);