implement browser code viewer
This commit is contained in:
parent
10ef547da7
commit
348ea6c49c
|
@ -2384,6 +2384,12 @@
|
||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"detect-browser": {
|
||||||
|
"version": "4.8.0",
|
||||||
|
"resolved": "https://registry.npm.taobao.org/detect-browser/download/detect-browser-4.8.0.tgz",
|
||||||
|
"integrity": "sha1-HXO9iMF76GaQGVDOCqrh7QYJAsY=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"detect-file": {
|
"detect-file": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"coffee-loader": "^0.9.0",
|
"coffee-loader": "^0.9.0",
|
||||||
"coffeescript": "^2.5.1",
|
"coffeescript": "^2.5.1",
|
||||||
"css-loader": "^3.4.2",
|
"css-loader": "^3.4.2",
|
||||||
|
"detect-browser": "^4.8.0",
|
||||||
"fast-xml-parser": "^3.16.0",
|
"fast-xml-parser": "^3.16.0",
|
||||||
"highlight.js": "^9.18.1",
|
"highlight.js": "^9.18.1",
|
||||||
"html-webpack-inline-source-plugin": "0.0.10",
|
"html-webpack-inline-source-plugin": "0.0.10",
|
||||||
|
|
|
@ -21,15 +21,18 @@ buildInvalidResponse = (msg) ->
|
||||||
new Response msg,
|
new Response msg,
|
||||||
status: 400
|
status: 400
|
||||||
|
|
||||||
|
buildFrontendResponse = ->
|
||||||
|
new Response indexHtml,
|
||||||
|
status: 200
|
||||||
|
headers:
|
||||||
|
'content-type': 'text/html'
|
||||||
|
|
||||||
handleRequest = (event) ->
|
handleRequest = (event) ->
|
||||||
# Handle request for static home page first
|
# Handle request for static home page first
|
||||||
if event.request.method == "GET"
|
if event.request.method == "GET"
|
||||||
parsedURL = new URL event.request.url
|
parsedURL = new URL event.request.url
|
||||||
if parsedURL.pathname in FRONTEND_PATHS
|
if parsedURL.pathname in FRONTEND_PATHS
|
||||||
return new Response indexHtml,
|
return buildFrontendResponse _
|
||||||
status: 200
|
|
||||||
headers:
|
|
||||||
'content-type': 'text/html'
|
|
||||||
|
|
||||||
# Validate file name first, since this is shared logic
|
# Validate file name first, since this is shared logic
|
||||||
file = util.getFileName event.request.url
|
file = util.getFileName event.request.url
|
||||||
|
@ -99,6 +102,14 @@ handleGET = (req, file) ->
|
||||||
return new Response "Something went wrong",
|
return new Response "Something went wrong",
|
||||||
status: resp.status
|
status: resp.status
|
||||||
|
|
||||||
|
# If the content is text, and the user is using a browser
|
||||||
|
# show frontend code viewer
|
||||||
|
if not req.url.endsWith 'original'
|
||||||
|
isText = util.isText resp.headers.get 'content-type'
|
||||||
|
isBrowser = util.isBrowser req
|
||||||
|
if isText and isBrowser
|
||||||
|
return buildFrontendResponse _
|
||||||
|
|
||||||
# Build response headers
|
# Build response headers
|
||||||
headers =
|
headers =
|
||||||
'content-length': resp.headers.get 'content-length'
|
'content-length': resp.headers.get 'content-length'
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { detect as detectBrowser } from 'detect-browser'
|
||||||
|
|
||||||
# Maimum upload size (in bytes)
|
# Maimum upload size (in bytes)
|
||||||
MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10 MB
|
MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10 MB
|
||||||
|
|
||||||
|
@ -34,18 +36,28 @@ idToPath = (id) ->
|
||||||
|
|
||||||
# Determine if we show something inline or not
|
# Determine if we show something inline or not
|
||||||
shouldShowInline = (mime) ->
|
shouldShowInline = (mime) ->
|
||||||
mime.startsWith('text/') or
|
isText(mime) or
|
||||||
mime.startsWith('image/') or
|
mime.startsWith('image/') or
|
||||||
mime.startsWith('audio/') or
|
mime.startsWith('audio/') or
|
||||||
mime.startsWith('video/') or
|
mime.startsWith('video/')
|
||||||
|
|
||||||
|
isText = (mime) ->
|
||||||
|
mime.startsWith('text/') or
|
||||||
mime == 'application/json' or
|
mime == 'application/json' or
|
||||||
mime == 'application/javascript'
|
mime == 'application/javascript'
|
||||||
|
|
||||||
|
# Determine if we consider the user a browser or not
|
||||||
|
isBrowser = (req) ->
|
||||||
|
b = detectBrowser req.headers.get 'user-agent'
|
||||||
|
(not b) or (b.name != 'searchbot')
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getFileName,
|
getFileName,
|
||||||
validateLength,
|
validateLength,
|
||||||
MAX_UPLOAD_SIZE,
|
MAX_UPLOAD_SIZE,
|
||||||
randomID,
|
randomID,
|
||||||
idToPath,
|
idToPath,
|
||||||
shouldShowInline
|
shouldShowInline,
|
||||||
|
isBrowser,
|
||||||
|
isText
|
||||||
}
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React from "react"
|
||||||
|
import { Redirect } from "react-router-dom"
|
||||||
|
import hljs from "highlight.js"
|
||||||
|
|
||||||
|
MAX_HIGHLIGHT_LENGTH = 10 * 1024 # 10 KiB
|
||||||
|
|
||||||
|
class CodeViewer extends React.Component
|
||||||
|
constructor: (props) ->
|
||||||
|
super props
|
||||||
|
@state =
|
||||||
|
code: "Loading..."
|
||||||
|
switchToHome: false
|
||||||
|
highlight: true
|
||||||
|
|
||||||
|
componentDidMount: ->
|
||||||
|
resp = await fetch "/paste/#{@props.match.params.id}?original"
|
||||||
|
resp = await resp.text()
|
||||||
|
if resp.length < MAX_HIGHLIGHT_LENGTH
|
||||||
|
resp = hljs.highlightAuto(resp).value
|
||||||
|
else
|
||||||
|
@setState
|
||||||
|
highlight: false
|
||||||
|
|
||||||
|
@setState
|
||||||
|
code: resp
|
||||||
|
|
||||||
|
render: ->
|
||||||
|
if @state.switchToHome
|
||||||
|
return <Redirect push to="/paste/text" />
|
||||||
|
|
||||||
|
<div className="content-pastebin">
|
||||||
|
<div
|
||||||
|
className="content-code-viewer"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if @state.highlight
|
||||||
|
<pre
|
||||||
|
dangerouslySetInnerHTML={{__html: @state.code}}
|
||||||
|
/>
|
||||||
|
else
|
||||||
|
<pre>{@state.code}</pre>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className="content-buttons">
|
||||||
|
<button
|
||||||
|
className="button-blue"
|
||||||
|
onClick={(ev) => @setState { switchToHome: true }}
|
||||||
|
>
|
||||||
|
New Paste
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
export default CodeViewer
|
|
@ -4,6 +4,7 @@ import { AnimatedSwitch } from 'react-router-transition'
|
||||||
import ReactModal from "react-modal"
|
import ReactModal from "react-modal"
|
||||||
import Pastebin from "./pastebin"
|
import Pastebin from "./pastebin"
|
||||||
import BinaryUpload from "./binaryUpload"
|
import BinaryUpload from "./binaryUpload"
|
||||||
|
import CodeViewer from "./codeViewer"
|
||||||
|
|
||||||
class Home extends React.Component
|
class Home extends React.Component
|
||||||
constructor: (props) ->
|
constructor: (props) ->
|
||||||
|
@ -36,6 +37,10 @@ class Home extends React.Component
|
||||||
exact path="/paste/binary"
|
exact path="/paste/binary"
|
||||||
component={() => <BinaryUpload openDialog={@openDialog}/>}
|
component={() => <BinaryUpload openDialog={@openDialog}/>}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/paste/:id"
|
||||||
|
component={CodeViewer}
|
||||||
|
/>
|
||||||
</AnimatedSwitch>
|
</AnimatedSwitch>
|
||||||
</Router>
|
</Router>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Some blend of both editable and pastebin style
|
||||||
|
.content-code-viewer {
|
||||||
|
display: block;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
text-align: left;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: monospace;
|
||||||
|
padding: 10px;
|
||||||
|
outline: none;
|
||||||
|
border: 1px solid $editable-color;
|
||||||
|
border-radius: $editable-radius;
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
@import './pastebin.scss';
|
@import './pastebin.scss';
|
||||||
@import './buttons.scss';
|
@import './buttons.scss';
|
||||||
@import './dropzone.scss';
|
@import './dropzone.scss';
|
||||||
|
@import './codeViewer.scss';
|
||||||
|
|
||||||
body, html {
|
body, html {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
Loading…
Reference in New Issue