mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-26 11:42:39 +00:00
Merge branch 'master' into addEditorThemes-#362
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/*eslint max-lines: ["warn", {"max": 400, "skipBlankLines": true, "skipComments": true}]*/
|
||||
/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/
|
||||
// Set working directory to project root
|
||||
process.chdir(`${__dirname}/..`);
|
||||
|
||||
@@ -399,7 +399,6 @@ app.get('/account', asyncHandler(async (req, res, next)=>{
|
||||
return next();
|
||||
}));
|
||||
|
||||
|
||||
const nodeEnv = config.get('node_env');
|
||||
const isLocalEnvironment = config.get('local_environments').includes(nodeEnv);
|
||||
// Local only
|
||||
@@ -416,8 +415,7 @@ if(isLocalEnvironment){
|
||||
|
||||
//Render the page
|
||||
const templateFn = require('./../client/template.js');
|
||||
app.use(asyncHandler(async (req, res, next)=>{
|
||||
|
||||
const renderPage = async (req, res)=>{
|
||||
// Create configuration object
|
||||
const configuration = {
|
||||
local : isLocalEnvironment,
|
||||
@@ -427,7 +425,7 @@ app.use(asyncHandler(async (req, res, next)=>{
|
||||
};
|
||||
const props = {
|
||||
version : require('./../package.json').version,
|
||||
url : req.originalUrl,
|
||||
url : req.customUrl || req.originalUrl,
|
||||
brew : req.brew,
|
||||
brews : req.brews,
|
||||
googleBrews : req.googleBrews,
|
||||
@@ -441,15 +439,20 @@ app.use(asyncHandler(async (req, res, next)=>{
|
||||
const page = await templateFn('homebrew', title, props)
|
||||
.catch((err)=>{
|
||||
console.log(err);
|
||||
return res.sendStatus(500);
|
||||
});
|
||||
return page;
|
||||
};
|
||||
|
||||
//Send rendered page
|
||||
app.use(asyncHandler(async (req, res, next)=>{
|
||||
const page = await renderPage(req, res);
|
||||
if(!page) return;
|
||||
res.send(page);
|
||||
}));
|
||||
|
||||
//v=====----- Error-Handling Middleware -----=====v//
|
||||
//Format Errors so all fields will be sent
|
||||
const replaceErrors = (key, value)=>{
|
||||
//Format Errors as plain objects so all fields will appear in the string sent
|
||||
const formatErrors = (key, value)=>{
|
||||
if(value instanceof Error) {
|
||||
const error = {};
|
||||
Object.getOwnPropertyNames(value).forEach(function (key) {
|
||||
@@ -461,13 +464,30 @@ const replaceErrors = (key, value)=>{
|
||||
};
|
||||
|
||||
const getPureError = (error)=>{
|
||||
return JSON.parse(JSON.stringify(error, replaceErrors));
|
||||
return JSON.parse(JSON.stringify(error, formatErrors));
|
||||
};
|
||||
|
||||
app.use((err, req, res, next)=>{
|
||||
const status = err.status || 500;
|
||||
app.use(async (err, req, res, next)=>{
|
||||
const status = err.status || err.code || 500;
|
||||
console.error(err);
|
||||
res.status(status).send(getPureError(err));
|
||||
|
||||
req.ogMeta = { ...defaultMetaTags,
|
||||
title : 'Error Page',
|
||||
description : 'Something went wrong!'
|
||||
};
|
||||
req.brew = {
|
||||
...err,
|
||||
title : 'Error - Something went wrong!',
|
||||
text : err.errors?.map((error)=>{return error.message;}).join('\n\n') || err.message || 'Unknown error!',
|
||||
status : status,
|
||||
HBErrorCode : err.HBErrorCode ?? '00',
|
||||
pureError : getPureError(err)
|
||||
};
|
||||
req.customUrl= '/error';
|
||||
|
||||
const page = await renderPage(req, res);
|
||||
if(!page) return;
|
||||
res.send(page);
|
||||
});
|
||||
|
||||
app.use((req, res)=>{
|
||||
|
||||
@@ -57,7 +57,14 @@ const api = {
|
||||
googleError = err;
|
||||
});
|
||||
// Throw any error caught while attempting to retrieve Google brew.
|
||||
if(googleError) throw googleError;
|
||||
if(googleError) {
|
||||
const reason = googleError.errors?.[0].reason;
|
||||
if(reason == 'notFound') {
|
||||
throw { ...googleError, HBErrorCode: '02', authors: stub?.authors, account: req.account?.username };
|
||||
} else {
|
||||
throw { ...googleError, HBErrorCode: '01' };
|
||||
}
|
||||
}
|
||||
// Combine the Homebrewery stub with the google brew, or if the stub doesn't exist just use the google brew
|
||||
stub = stub ? _.assign({ ...api.excludeStubProps(stub), stubbed: true }, api.excludeGoogleProps(googleBrew)) : googleBrew;
|
||||
}
|
||||
@@ -65,14 +72,16 @@ const api = {
|
||||
const isAuthor = stub?.authors?.includes(req.account?.username);
|
||||
const isInvited = stub?.invitedAuthors?.includes(req.account?.username);
|
||||
if(accessType === 'edit' && (authorsExist && !(isAuthor || isInvited))) {
|
||||
throw `The current logged in user does not have editor access to this brew.
|
||||
|
||||
If you believe you should have access to this brew, ask the file owner to invite you as an author by opening the brew, viewing the Properties tab, and adding your username to the "invited authors" list. You can then try to access this document again.`;
|
||||
const accessError = { name: 'Access Error', status: 401 };
|
||||
if(req.account){
|
||||
throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title };
|
||||
}
|
||||
throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title };
|
||||
}
|
||||
|
||||
// If after all of that we still don't have a brew, throw an exception
|
||||
if(!stub && !stubOnly) {
|
||||
throw 'Brew not found in Homebrewery database or Google Drive';
|
||||
throw { name: 'BrewLoad Error', message: 'Brew not found', status: 404, HBErrorCode: '05', accessType: accessType, brewId: id };
|
||||
}
|
||||
|
||||
// Clean up brew: fill in missing fields with defaults / fix old invalid values
|
||||
@@ -181,7 +190,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
||||
saved = await newHomebrew.save()
|
||||
.catch((err)=>{
|
||||
console.error(err, err.toString(), err.stack);
|
||||
throw `Error while creating new brew, ${err.toString()}`;
|
||||
throw { name: 'BrewSave Error', message: `Error while creating new brew, ${err.toString()}`, status: 500, HBErrorCode: '06' };
|
||||
});
|
||||
if(!saved) return;
|
||||
saved = saved.toObject();
|
||||
@@ -283,10 +292,13 @@ If you believe you should have access to this brew, ask the file owner to invite
|
||||
try {
|
||||
await api.getBrew('edit')(req, res, ()=>{});
|
||||
} catch (err) {
|
||||
const { id, googleId } = api.getId(req);
|
||||
console.warn(`No google brew found for id ${googleId}, the stub with id ${id} will be deleted.`);
|
||||
await HomebrewModel.deleteOne({ editId: id });
|
||||
return next();
|
||||
// Only if the error code is HBErrorCode '02', that is, Google returned "404 - Not Found"
|
||||
if(err.HBErrorCode == '02') {
|
||||
const { id, googleId } = api.getId(req);
|
||||
console.warn(`No google brew found for id ${googleId}, the stub with id ${id} will be deleted.`);
|
||||
await HomebrewModel.deleteOne({ editId: id });
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
let brew = req.brew;
|
||||
@@ -308,7 +320,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
||||
await HomebrewModel.deleteOne({ _id: brew._id })
|
||||
.catch((err)=>{
|
||||
console.error(err);
|
||||
throw { status: 500, message: 'Error while removing' };
|
||||
throw { name: 'BrewDelete Error', message: 'Error while removing', status: 500, HBErrorCode: '07', brewId: brew._id };
|
||||
});
|
||||
} else {
|
||||
if(shouldDeleteGoogleBrew) {
|
||||
@@ -320,7 +332,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
||||
brew.markModified('authors'); //Mongo will not properly update arrays without markModified()
|
||||
await brew.save()
|
||||
.catch((err)=>{
|
||||
throw { status: 500, message: err };
|
||||
throw { name: 'BrewAuthorDelete Error', message: err, status: 500, HBErrorCode: '08', brewId: brew._id };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('Tests for api', ()=>{
|
||||
|
||||
describe('getBrew', ()=>{
|
||||
const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
|
||||
const notFoundError = 'Brew not found in Homebrewery database or Google Drive';
|
||||
const notFoundError = { HBErrorCode: '05', message: 'Brew not found', name: 'BrewLoad Error', status: 404, accessType: 'share', brewId: '1' };
|
||||
|
||||
it('returns middleware', ()=>{
|
||||
const getFn = api.getBrew('share');
|
||||
@@ -183,7 +183,7 @@ describe('Tests for api', ()=>{
|
||||
expect(next).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('throws if invalid author', async ()=>{
|
||||
it('throws if not logged in as author', async ()=>{
|
||||
api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
|
||||
model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', authors: ['a'] }));
|
||||
|
||||
@@ -197,9 +197,24 @@ describe('Tests for api', ()=>{
|
||||
err = e;
|
||||
}
|
||||
|
||||
expect(err).toEqual(`The current logged in user does not have editor access to this brew.
|
||||
expect(err).toEqual({ HBErrorCode: '04', message: 'User is not logged in', name: 'Access Error', status: 401, brewTitle: 'test brew', authors: ['a'] });
|
||||
});
|
||||
|
||||
If you believe you should have access to this brew, ask the file owner to invite you as an author by opening the brew, viewing the Properties tab, and adding your username to the "invited authors" list. You can then try to access this document again.`);
|
||||
it('throws if logged in as invalid author', async ()=>{
|
||||
api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
|
||||
model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', authors: ['a'] }));
|
||||
|
||||
const fn = api.getBrew('edit', true);
|
||||
const req = { brew: {}, account: { username: 'b' } };
|
||||
|
||||
let err;
|
||||
try {
|
||||
await fn(req, null, null);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
expect(err).toEqual({ HBErrorCode: '03', message: 'User is not an Author', name: 'Access Error', status: 401, brewTitle: 'test brew', authors: ['a'] });
|
||||
});
|
||||
|
||||
it('does not throw if no authors', async ()=>{
|
||||
@@ -545,7 +560,7 @@ brew`);
|
||||
|
||||
describe('deleteBrew', ()=>{
|
||||
it('should handle case where fetching the brew returns an error', async ()=>{
|
||||
api.getBrew = jest.fn(()=>async ()=>{ throw 'err'; });
|
||||
api.getBrew = jest.fn(()=>async ()=>{ throw { message: 'err', HBErrorCode: '02' }; });
|
||||
api.getId = jest.fn(()=>({ id: '1', googleId: '2' }));
|
||||
model.deleteOne = jest.fn(async ()=>{});
|
||||
const next = jest.fn(()=>{});
|
||||
|
||||
Reference in New Issue
Block a user