0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 18:32:41 +00:00

Merge branch 'master' into disable-changes-from-non-authors

This commit is contained in:
Charlie Humphreys
2022-12-12 10:39:08 -06:00
13 changed files with 1337 additions and 1120 deletions

View File

@@ -77,6 +77,14 @@ const faqText = require('fs').readFileSync('faq.md', 'utf8');
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
const defaultMetaTags = {
site_name : 'The Homebrewery - Make your Homebrew content look legit!',
title : 'The Homebrewery',
description : 'A NaturalCrit Tool for creating authentic Homebrews using Markdown.',
image : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
};
//Robots.txt
app.get('/robots.txt', (req, res)=>{
return res.sendFile(`robots.txt`, { root: process.cwd() });
@@ -89,13 +97,11 @@ app.get('/', (req, res, next)=>{
renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : 'Homepage',
description : 'Homepage',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
description : 'Homepage'
};
splitTextStyleAndMetadata(req.brew);
return next();
});
@@ -107,13 +113,11 @@ app.get('/legacy', (req, res, next)=>{
renderer : 'legacy'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : 'Homepage (Legacy)',
description : 'Homepage',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
description : 'Homepage'
};
splitTextStyleAndMetadata(req.brew);
return next();
});
@@ -125,13 +129,11 @@ app.get('/migrate', (req, res, next)=>{
renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : 'v3 Migration Guide',
description : 'A brief guide to converting Legacy documents to the v3 renderer.',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
description : 'A brief guide to converting Legacy documents to the v3 renderer.'
};
splitTextStyleAndMetadata(req.brew);
return next();
});
@@ -144,13 +146,11 @@ app.get('/changelog', async (req, res, next)=>{
renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : 'Changelog',
description : 'Development changelog.',
thumbnail : null,
type : 'website'
description : 'Development changelog.'
};
splitTextStyleAndMetadata(req.brew);
return next();
});
@@ -163,12 +163,9 @@ app.get('/faq', async (req, res, next)=>{
renderer : 'V3'
},
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : 'FAQ',
description : 'Frequently Asked Questions',
thumbnail : `${config.get('publicUrl')}/thumbnail.png`,
type : 'website'
description : 'Frequently Asked Questions'
};
splitTextStyleAndMetadata(req.brew);
@@ -194,12 +191,19 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
sanitizeBrew(brew, 'share');
const prefix = 'HB - ';
const encodeRFC3986ValueChars = (str)=>{
return (
encodeURIComponent(str)
.replace(/[!'()*]/g, (char)=>{`%${char.charCodeAt(0).toString(16).toUpperCase()}`;})
);
};
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; };
res.set({
'Cache-Control' : 'no-cache',
'Content-Type' : 'text/plain',
'Content-Disposition' : `attachment; filename="${fileName}.txt"`
'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.txt`
});
res.status(200).send(brew.text);
});
@@ -208,12 +212,10 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
app.get('/user/:username', async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username);
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : `${req.params.username}'s Collection`,
description : 'View my collection of homebrew on the Homebrewery.',
image : null,
type : 'website' // or 'profile'?
description : 'View my collection of homebrew on the Homebrewery.'
// type : could be 'profile'?
};
const fields = [
@@ -274,13 +276,13 @@ 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;
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
image : req.brew.thumbnail || null,
image : req.brew.thumbnail || defaultMetaTags.image,
type : 'article'
};
sanitizeBrew(req.brew, 'edit');
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.
@@ -292,13 +294,12 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew);
req.brew.title = `CLONE - ${req.brew.title}`;
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : 'New',
description : 'Start crafting your homebrew on the Homebrewery!',
image : null,
type : 'website'
description : 'Start crafting your homebrew on the Homebrewery!'
};
return next();
});
@@ -306,11 +307,10 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
const { brew } = req;
req.ogMeta = {
siteName : 'The Homebrewery - Make your Homebrew content look legit!',
req.ogMeta = { ...defaultMetaTags,
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
image : req.brew.thumbnail || null,
image : req.brew.thumbnail || defaultMetaTags.image,
type : 'article'
};
@@ -340,11 +340,11 @@ app.get('/account', asyncHandler(async (req, res, next)=>{
data.title = 'Account Information Page';
let auth;
let files;
let googleCount = [];
if(req.account) {
if(req.account.googleId) {
try {
auth = await GoogleActions.authCheck(req.account, res);
auth = await GoogleActions.authCheck(req.account, res, false);
} catch (e) {
auth = undefined;
console.log('Google auth check failed!');
@@ -352,9 +352,9 @@ app.get('/account', asyncHandler(async (req, res, next)=>{
}
if(auth.credentials.access_token) {
try {
files = await GoogleActions.listGoogleBrews(auth);
googleCount = await GoogleActions.listGoogleBrews(auth);
} catch (e) {
files = undefined;
googleCount = undefined;
console.log('List Google files failed!');
console.log(e);
}
@@ -362,22 +362,29 @@ app.get('/account', asyncHandler(async (req, res, next)=>{
}
const query = { authors: req.account.username, googleId: { $exists: false } };
const brews = await HomebrewModel.find(query, 'id')
const mongoCount = await HomebrewModel.countDocuments(query)
.catch((err)=>{
mongoCount = 0;
console.log(err);
});
data.uiItems = {
username : req.account.username,
issued : req.account.issued,
mongoCount : brews.length,
googleId : Boolean(req.account.googleId),
authCheck : Boolean(req.account.googleId && auth.credentials.access_token),
fileCount : files?.length || '-'
username : req.account.username,
issued : req.account.issued,
googleId : Boolean(req.account.googleId),
authCheck : Boolean(req.account.googleId && auth.credentials.access_token),
mongoCount : mongoCount,
googleCount : googleCount?.length
};
}
req.brew = data;
req.ogMeta = { ...defaultMetaTags,
title : `Account Page`,
description : null
};
return next();
}));

View File

@@ -5,24 +5,28 @@ const { nanoid } = require('nanoid');
const token = require('./token.js');
const config = require('./config.js');
const keys = typeof(config.get('service_account')) == 'string' ?
JSON.parse(config.get('service_account')) :
config.get('service_account');
let serviceAuth;
try {
serviceAuth = google.auth.fromJSON(keys);
serviceAuth.scopes = [
'https://www.googleapis.com/auth/drive'
];
} catch (err) {
console.warn(err);
console.log('Please make sure that a Google Service Account is set up properly in your config files.');
if(!config.get('service_account')){
console.log('No Google Service Account in config files - Google Drive integration will not be available.');
} else {
const keys = typeof(config.get('service_account')) == 'string' ?
JSON.parse(config.get('service_account')) :
config.get('service_account');
try {
serviceAuth = google.auth.fromJSON(keys);
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
} catch (err) {
console.warn(err);
console.log('Please make sure the Google Service Account is set up properly in your config files.');
}
}
google.options({ auth: serviceAuth || config.get('google_api_key') });
const GoogleActions = {
authCheck : (account, res)=>{
authCheck : (account, res, updateTokens=true)=>{
if(!account || !account.googleId){ // If not signed into Google
const err = new Error('Not Signed In');
err.status = 401;
@@ -40,7 +44,7 @@ const GoogleActions = {
refresh_token : account.googleRefreshToken
});
oAuth2Client.on('tokens', (tokens)=>{
updateTokens && oAuth2Client.on('tokens', (tokens)=>{
if(tokens.refresh_token) {
account.googleRefreshToken = tokens.refresh_token;
}
@@ -249,7 +253,6 @@ const GoogleActions = {
text : file.data,
description : obj.data.description,
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,

View File

@@ -27,7 +27,7 @@ const getId = (req)=>{
return { id, googleId };
};
const getBrew = (accessType, fetchGoogle = true)=>{
const getBrew = (accessType, stubOnly = false)=>{
// Create middleware with the accessType passed in as part of the scope
return async (req, res, next)=>{
// Get relevant IDs for the brew
@@ -48,7 +48,7 @@ const getBrew = (accessType, fetchGoogle = true)=>{
}
// If there is a google id, try to find the google brew
if(fetchGoogle && (googleId || stub?.googleId)) {
if(!stubOnly && (googleId || stub?.googleId)) {
let googleError;
const googleBrew = await GoogleActions.getGoogleBrew(googleId || stub?.googleId, id, accessType)
.catch((err)=>{
@@ -62,7 +62,7 @@ const getBrew = (accessType, fetchGoogle = true)=>{
}
// If after all of that we still don't have a brew, throw an exception
if(!stub && fetchGoogle) {
if(!stub && !stubOnly) {
throw 'Brew not found in Homebrewery database or Google Drive';
}
@@ -237,10 +237,6 @@ const updateBrew = async (req, res)=>{
if(req.account) {
brew.authors = _.uniq(_.concat(brew.authors, req.account.username));
}
// we need the tag type change in both getBrew and here to handle the case where we don't have a stub on which to perform the modification
if(typeof brew.tags === 'string') {
brew.tags = [];
}
// define a function to catch our save errors
const saveError = (err)=>{
@@ -254,14 +250,16 @@ const updateBrew = async (req, res)=>{
brew = saved?.toObject();
} else {
// if the brew does have a stub id, update it using the stub id as the key.
saved = await HomebrewModel.updateOne({ _id: brew._id }, brew).catch(saveError);
brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew);
saved = await brew.save()
.catch(saveError);
}
if(!saved) return;
// Call and wait for afterSave to complete
const after = await afterSave();
if(!after) return;
res.status(200).send(brew);
res.status(200).send(saved);
};
const deleteGoogleBrew = async (account, id, editId, res)=>{
@@ -331,8 +329,8 @@ const deleteBrew = async (req, res, next)=>{
};
router.post('/api', asyncHandler(newBrew));
router.put('/api/:id', asyncHandler(getBrew('edit', false)), asyncHandler(updateBrew));
router.put('/api/update/:id', asyncHandler(getBrew('edit', false)), asyncHandler(updateBrew));
router.put('/api/:id', asyncHandler(getBrew('edit', true)), asyncHandler(updateBrew));
router.put('/api/update/:id', asyncHandler(getBrew('edit', true)), asyncHandler(updateBrew));
router.delete('/api/:id', asyncHandler(deleteBrew));
router.get('/api/remove/:id', asyncHandler(deleteBrew));