diff --git a/package-lock.json b/package-lock.json index b81c7c0..b70878b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2384,6 +2384,12 @@ "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": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", diff --git a/package.json b/package.json index a13ec8f..f380459 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "coffee-loader": "^0.9.0", "coffeescript": "^2.5.1", "css-loader": "^3.4.2", + "detect-browser": "^4.8.0", "fast-xml-parser": "^3.16.0", "highlight.js": "^9.18.1", "html-webpack-inline-source-plugin": "0.0.10", diff --git a/src/index.coffee b/src/index.coffee index b5d6200..cf20d21 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -21,15 +21,18 @@ buildInvalidResponse = (msg) -> new Response msg, status: 400 +buildFrontendResponse = -> + new Response indexHtml, + status: 200 + headers: + 'content-type': 'text/html' + handleRequest = (event) -> # Handle request for static home page first if event.request.method == "GET" parsedURL = new URL event.request.url if parsedURL.pathname in FRONTEND_PATHS - return new Response indexHtml, - status: 200 - headers: - 'content-type': 'text/html' + return buildFrontendResponse _ # Validate file name first, since this is shared logic file = util.getFileName event.request.url @@ -99,6 +102,14 @@ handleGET = (req, file) -> return new Response "Something went wrong", 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 headers = 'content-length': resp.headers.get 'content-length' diff --git a/src/util.coffee b/src/util.coffee index 24f9e7b..0f751cf 100644 --- a/src/util.coffee +++ b/src/util.coffee @@ -1,3 +1,5 @@ +import { detect as detectBrowser } from 'detect-browser' + # Maimum upload size (in bytes) MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10 MB @@ -34,18 +36,28 @@ idToPath = (id) -> # Determine if we show something inline or not shouldShowInline = (mime) -> - mime.startsWith('text/') or + isText(mime) or mime.startsWith('image/') or mime.startsWith('audio/') or - mime.startsWith('video/') or + mime.startsWith('video/') + +isText = (mime) -> + mime.startsWith('text/') or mime == 'application/json' or 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 { getFileName, validateLength, MAX_UPLOAD_SIZE, randomID, idToPath, - shouldShowInline + shouldShowInline, + isBrowser, + isText } \ No newline at end of file diff --git a/src/web/codeViewer.coffee b/src/web/codeViewer.coffee new file mode 100644 index 0000000..7a30275 --- /dev/null +++ b/src/web/codeViewer.coffee @@ -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 + +
+
+ { + if @state.highlight +
+          else
+            
{@state.code}
+ } +
+
+ +
+
+ +export default CodeViewer \ No newline at end of file diff --git a/src/web/home.coffee b/src/web/home.coffee index 244f4f2..4407968 100644 --- a/src/web/home.coffee +++ b/src/web/home.coffee @@ -4,6 +4,7 @@ import { AnimatedSwitch } from 'react-router-transition' import ReactModal from "react-modal" import Pastebin from "./pastebin" import BinaryUpload from "./binaryUpload" +import CodeViewer from "./codeViewer" class Home extends React.Component constructor: (props) -> @@ -36,6 +37,10 @@ class Home extends React.Component exact path="/paste/binary" component={() => } /> + diff --git a/src/web/styles/codeViewer.scss b/src/web/styles/codeViewer.scss new file mode 100644 index 0000000..007ac2c --- /dev/null +++ b/src/web/styles/codeViewer.scss @@ -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; + } +} \ No newline at end of file diff --git a/src/web/styles/index.scss b/src/web/styles/index.scss index a18e9fb..e01a726 100644 --- a/src/web/styles/index.scss +++ b/src/web/styles/index.scss @@ -7,6 +7,7 @@ @import './pastebin.scss'; @import './buttons.scss'; @import './dropzone.scss'; +@import './codeViewer.scss'; body, html { margin: 0px;