0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-05 10:12:41 +00:00

Added sanitizeHtml function to Homebrew model to generate HTML from the brew for the source page.

Moved `source` page generation function to a new function module. Added option to function to create plain text download with a sanitized filename and made it accessible from a new page: `download`.
Added the `download` item to the BrewItem so it appears on each brew on the User page.
Added `sanitize-filename` dependency to `package.json`.
This commit is contained in:
G.Ambatte
2021-01-21 00:02:15 +13:00
parent 7fccb7e03e
commit e8135fcbb4
7 changed files with 79 additions and 67 deletions

View File

@@ -62,7 +62,7 @@ const SharePage = createClass({
<Nav.item href={`/source/${this.processShareId()}`} color='teal' icon='fa-code'> <Nav.item href={`/source/${this.processShareId()}`} color='teal' icon='fa-code'>
view source view source
</Nav.item> </Nav.item>
<Nav.item href={`/source_dl/${this.processShareId()}`} color='red' icon='fa-download'> <Nav.item href={`/download/${this.processShareId()}`} color='red' icon='fa-download'>
download source download source
</Nav.item> </Nav.item>
<RecentNavItem brew={this.props.brew} storageKey='view' /> <RecentNavItem brew={this.props.brew} storageKey='view' />

View File

@@ -0,0 +1,56 @@
const HomebrewModel = require.main.require('./server/homebrew.model.js').model;
const sanitizeFilename = require('sanitize-filename');
const shareFunction = function(req, res, type) {
if(req.params.id.length > 12) {
const googleId = req.params.id.slice(0, -12);
const shareId = req.params.id.slice(-12);
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
.then((brew)=>{
if(type == 'source') {
return res.status(200).send(brew.sanitizeHtml());
} else if(type == 'download') {
let fileName = sanitizeFilename(brew.title);
fileName = fileName.replaceAll(' ', '-');
res.status(200);
res.set({
'Cache-Control' : 'no-cache',
'Content-Type' : 'text/plain',
'Content-Disposition' : `attachment; filename="HomeBrewery-${fileName}.txt"`
});
return res.send(brew.text);
} else {
console.log('Unhandled source share type');
}
})
.catch((err)=>{
console.log(err);
return res.status(400).send('Can\'t get brew from Google');
});
} else {
HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{
if(type == 'source') {
return res.status(200).send(brew.sanitizeHtml());
} else if(type == 'download') {
let fileName = sanitizeFilename(brew.title);
fileName = fileName.replaceAll(' ', '-');
res.status(200);
res.set({
'Cache-Control' : 'no-cache',
'Content-Type' : 'text/plain',
'Content-Disposition' : `attachment; filename="HomeBrewery-${fileName}.txt"`
});
return res.send(brew.text);
} else {
console.log('Unhandled share type');
}
})
.catch((err)=>{
console.log(err);
return res.status(404).send('Could not find Homebrew with that id');
});
}
};
module.exports = shareFunction;

View File

@@ -1,14 +0,0 @@
const sourceDL = function(res, brew) {
const fileName = brew.title.replaceAll(' ', '-').replaceAll(':', '').replaceAll('/', '');
res.status(200);
res.set({
'Cache-Control' : 'no-cache',
'Content-Type' : 'text/plain',
'Content-Disposition' : `attachment; filename="HomeBrewery-${fileName}.txt"`
});
return res;
};
module.exports = sourceDL;

View File

@@ -65,7 +65,7 @@ const BrewItem = createClass({
</a>; </a>;
}, },
renderShareLink : function(){ renderSourceLink : function(path, icon){
if(!this.props.brew.shareId) return; if(!this.props.brew.shareId) return;
let shareLink = this.props.brew.shareId; let shareLink = this.props.brew.shareId;
@@ -73,8 +73,8 @@ const BrewItem = createClass({
shareLink = this.props.brew.googleId + shareLink; shareLink = this.props.brew.googleId + shareLink;
} }
return <a href={`/share/${shareLink}`} target='_blank' rel='noopener noreferrer'> return <a href={`/${path}/${shareLink}`} target='_blank' rel='noopener noreferrer'>
<i className='fa fa-share-alt' /> <i className={`fa ${icon}`} />
</a>; </a>;
}, },
@@ -107,8 +107,9 @@ const BrewItem = createClass({
</div> </div>
<div className='links'> <div className='links'>
{this.renderShareLink()} {this.renderSourceLink('share', 'fa-share-alt')}
{this.renderEditLink()} {this.renderEditLink()}
{this.renderSourceLink('download', 'fa-download')}
{this.renderDeleteBrewLink()} {this.renderDeleteBrewLink()}
</div> </div>
</div>; </div>;

View File

@@ -66,6 +66,7 @@
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-frame-component": "4.1.3", "react-frame-component": "4.1.3",
"react-router-dom": "5.2.0", "react-router-dom": "5.2.0",
"sanitize-filename": "1.6.3",
"superagent": "^6.1.0", "superagent": "^6.1.0",
"vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b" "vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b"
}, },

View File

@@ -7,6 +7,8 @@ const app = express();
const homebrewApi = require('./server/homebrew.api.js'); const homebrewApi = require('./server/homebrew.api.js');
const GoogleActions = require('./server/googleActions.js'); const GoogleActions = require('./server/googleActions.js');
const shareFunction = require ('./client/homebrew/pages/sharePage/sourceFunctions.jsx');
// Serve brotli-compressed static files if available // Serve brotli-compressed static files if available
app.use('/', expressStaticGzip(`${__dirname}/build`, { app.use('/', expressStaticGzip(`${__dirname}/build`, {
enableBrotli : true, enableBrotli : true,
@@ -70,57 +72,12 @@ app.get('/robots.txt', (req, res)=>{
//Source page //Source page
app.get('/source/:id', (req, res)=>{ app.get('/source/:id', (req, res)=>{
if(req.params.id.length > 12) { return shareFunction(req, res, 'source');
const googleId = req.params.id.slice(0, -12);
const shareId = req.params.id.slice(-12);
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
.then((brew)=>{
const text = brew.text.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
return res.status(200).send(`<code><pre style="white-space: pre-wrap;">${text}</pre></code>`);
})
.catch((err)=>{
console.log(err);
return res.status(400).send('Can\'t get brew from Google');
});
} else {
HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{
const text = brew.text.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
return res.status(200).send(`<code><pre style="white-space: pre-wrap;">${text}</pre></code>`);
})
.catch((err)=>{
console.log(err);
return res.status(404).send('Could not find Homebrew with that id');
});
}
}); });
//Source download page //Source download page
app.get('/source_dl/:id', (req, res)=>{ app.get('/download/:id', (req, res)=>{
const sourceDL = require ('./client/homebrew/pages/sharePage/source_dl.jsx'); return shareFunction(req, res, 'download');
if(req.params.id.length > 12) {
const googleId = req.params.id.slice(0, -12);
const shareId = req.params.id.slice(-12);
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
.then((brew)=>{
res = sourceDL(res, brew);
return res.send(brew.text);
})
.catch((err)=>{
console.log(err);
return res.status(400).send('Can\'t get brew from Google');
});
} else {
HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{
res = sourceDL(res, brew);
return res.send(brew.text);
})
.catch((err)=>{
console.log(err);
return res.status(404).send('Could not find Homebrew with that id');
});
}
}); });
//User Page //User Page

View File

@@ -34,6 +34,17 @@ HomebrewSchema.methods.sanatize = function(full=false){
return brew; return brew;
}; };
HomebrewSchema.methods.sanitizeHtml = function(){
const brew = this.toJSON();
const replaceStrings = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
text = brew.text;
for (const replaceStr in replaceStrings) {
text = text.replaceAll(replaceStr, replaceStrings[replaceStr]);
}
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
return text;
};
HomebrewSchema.methods.increaseView = async function(){ HomebrewSchema.methods.increaseView = async function(){
this.lastViewed = new Date(); this.lastViewed = new Date();
this.views = this.views + 1; this.views = this.views + 1;