mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-07 01:12:44 +00:00
Merge pull request #1198 from G-Ambatte/sourceDL
Download Brew Source as Text File
This commit is contained in:
@@ -63,6 +63,9 @@ const SharePage = createClass({
|
|||||||
<Nav.item href={`/source/${this.processShareId()}`} color='teal' icon='fas fa-code'>
|
<Nav.item href={`/source/${this.processShareId()}`} color='teal' icon='fas fa-code'>
|
||||||
source
|
source
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
|
<Nav.item href={`/download/${this.processShareId()}`} color='red' icon='fas fa-download'>
|
||||||
|
download
|
||||||
|
</Nav.item>
|
||||||
<RecentNavItem brew={this.props.brew} storageKey='view' />
|
<RecentNavItem brew={this.props.brew} storageKey='view' />
|
||||||
<Account />
|
<Account />
|
||||||
</Nav.section>
|
</Nav.section>
|
||||||
|
|||||||
@@ -78,6 +78,19 @@ const BrewItem = createClass({
|
|||||||
</a>;
|
</a>;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderDownloadLink : function(){
|
||||||
|
if(!this.props.brew.shareId) return;
|
||||||
|
|
||||||
|
let shareLink = this.props.brew.shareId;
|
||||||
|
if(this.props.brew.googleId) {
|
||||||
|
shareLink = this.props.brew.googleId + shareLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <a href={`/download/${shareLink}`}>
|
||||||
|
<i className='fas fa-download' />
|
||||||
|
</a>;
|
||||||
|
},
|
||||||
|
|
||||||
renderGoogleDriveIcon : function(){
|
renderGoogleDriveIcon : function(){
|
||||||
if(!this.props.brew.gDrive) return;
|
if(!this.props.brew.gDrive) return;
|
||||||
|
|
||||||
@@ -109,6 +122,7 @@ const BrewItem = createClass({
|
|||||||
<div className='links'>
|
<div className='links'>
|
||||||
{this.renderShareLink()}
|
{this.renderShareLink()}
|
||||||
{this.renderEditLink()}
|
{this.renderEditLink()}
|
||||||
|
{this.renderDownloadLink()}
|
||||||
{this.renderDeleteBrewLink()}
|
{this.renderDeleteBrewLink()}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
box-sizing : border-box;
|
box-sizing : border-box;
|
||||||
overflow : hidden;
|
overflow : hidden;
|
||||||
width : 48%;
|
width : 48%;
|
||||||
min-height : 80px;
|
min-height : 105px;
|
||||||
margin-right : 15px;
|
margin-right : 15px;
|
||||||
margin-bottom : 15px;
|
margin-bottom : 15px;
|
||||||
padding : 5px 15px 5px 8px;
|
padding : 5px 15px 5px 8px;
|
||||||
|
|||||||
21
package-lock.json
generated
21
package-lock.json
generated
@@ -6592,6 +6592,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"sanitize-filename": {
|
||||||
|
"version": "1.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
|
||||||
|
"integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
|
||||||
|
"requires": {
|
||||||
|
"truncate-utf8-bytes": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"saslprep": {
|
"saslprep": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
@@ -7333,6 +7341,14 @@
|
|||||||
"nopt": "~1.0.10"
|
"nopt": "~1.0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"truncate-utf8-bytes": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=",
|
||||||
|
"requires": {
|
||||||
|
"utf8-byte-length": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
@@ -7589,6 +7605,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
||||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
|
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
|
||||||
},
|
},
|
||||||
|
"utf8-byte-length": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E="
|
||||||
|
},
|
||||||
"util": {
|
"util": {
|
||||||
"version": "0.10.4",
|
"version": "0.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||||
|
|||||||
@@ -67,6 +67,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"
|
||||||
},
|
},
|
||||||
|
|||||||
73
server.js
73
server.js
@@ -1,4 +1,4 @@
|
|||||||
/*eslint max-lines: ["warn", {"max": 250, "skipBlankLines": true, "skipComments": true}]*/
|
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const jwt = require('jwt-simple');
|
const jwt = require('jwt-simple');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@@ -7,6 +7,7 @@ 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 serveCompressedStaticAssets = require('./server/static-assets.mv.js');
|
const serveCompressedStaticAssets = require('./server/static-assets.mv.js');
|
||||||
|
const sanitizeFilename = require('sanitize-filename');
|
||||||
|
|
||||||
app.use('/', serveCompressedStaticAssets(`${__dirname}/build`));
|
app.use('/', serveCompressedStaticAssets(`${__dirname}/build`));
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ app.get('/robots.txt', (req, res)=>{
|
|||||||
return res.sendFile(`${__dirname}/robots.txt`);
|
return res.sendFile(`${__dirname}/robots.txt`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//Source page
|
//Source page
|
||||||
app.get('/source/:id', (req, res)=>{
|
app.get('/source/:id', (req, res)=>{
|
||||||
if(req.params.id.length > 12) {
|
if(req.params.id.length > 12) {
|
||||||
@@ -71,8 +73,13 @@ app.get('/source/:id', (req, res)=>{
|
|||||||
const shareId = req.params.id.slice(-12);
|
const shareId = req.params.id.slice(-12);
|
||||||
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
GoogleActions.readFileMetadata(config.get('google_api_key'), googleId, shareId, 'share')
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
const text = brew.text.replaceAll('<', '<').replaceAll('>', '>');
|
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
||||||
return res.send(`<code><pre style="white-space: pre-wrap;">${text}</pre></code>`);
|
let 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>`;
|
||||||
|
res.status(200).send(text);
|
||||||
})
|
})
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -80,14 +87,60 @@ app.get('/source/:id', (req, res)=>{
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
HomebrewModel.get({ shareId: req.params.id })
|
HomebrewModel.get({ shareId: req.params.id })
|
||||||
.then((brew)=>{
|
.then((brew)=>{
|
||||||
const text = brew.text.replaceAll('<', '<').replaceAll('>', '>');
|
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
||||||
return res.send(`<code><pre style="white-space: pre-wrap;">${text}</pre></code>`);
|
let text = brew.text;
|
||||||
})
|
for (const replaceStr in replaceStrings) {
|
||||||
.catch((err)=>{
|
text = text.replaceAll(replaceStr, replaceStrings[replaceStr]);
|
||||||
console.log(err);
|
}
|
||||||
return res.status(404).send('Could not find Homebrew with that id');
|
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
|
||||||
|
res.status(200).send(text);
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err);
|
||||||
|
return res.status(404).send('Could not find Homebrew with that id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Download brew source page
|
||||||
|
app.get('/download/:id', (req, res)=>{
|
||||||
|
const prefix = 'HB - ';
|
||||||
|
|
||||||
|
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)=>{
|
||||||
|
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"`
|
||||||
});
|
});
|
||||||
|
res.status(200).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)=>{
|
||||||
|
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"`
|
||||||
|
});
|
||||||
|
res.status(200).send(brew.text);
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log(err);
|
||||||
|
return res.status(404).send('Could not find Homebrew with that id');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -259,8 +259,12 @@ GoogleActions = {
|
|||||||
throw ('Share ID does not match');
|
throw ('Share ID does not match');
|
||||||
}
|
}
|
||||||
|
|
||||||
//Access actual file with service account. Just api key is causing "automated query" errors.
|
//Access file using service account. Using API key only causes "automated query" lockouts after a while.
|
||||||
const keys = JSON.parse(config.get('service_account'));
|
|
||||||
|
const keys = typeof(config.get('service_account')) == 'string' ?
|
||||||
|
JSON.parse(config.get('service_account')) :
|
||||||
|
config.get('service_account');
|
||||||
|
|
||||||
const serviceAuth = google.auth.fromJSON(keys);
|
const serviceAuth = google.auth.fromJSON(keys);
|
||||||
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user