simplify ui
This commit is contained in:
parent
dae61ea68a
commit
d7572abc72
15264
assets/package-lock.json
generated
15264
assets/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -3,17 +3,22 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bootstrap": "^4.5.2",
|
||||||
|
"crypto-js": "^4.0.0",
|
||||||
"react": "^16.9.0",
|
"react": "^16.9.0",
|
||||||
|
"react-bootstrap": "^1.3.0",
|
||||||
"react-dom": "^16.9.0",
|
"react-dom": "^16.9.0",
|
||||||
|
"react-highlight": "^0.12.0",
|
||||||
|
"react-markdown": "^4.3.1",
|
||||||
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "^3.1.1",
|
"react-scripts": "^3.1.1",
|
||||||
"sour-is": "file:src/vendor/sour-is"
|
"seedrandom": "^3.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test --env=jsdom",
|
"test": "react-scripts test --env=jsdom",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject"
|
||||||
"postinstall": "npm install src/vendor/sour-is"
|
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|
|
@ -2,10 +2,10 @@ import React, { Component } from 'react';
|
||||||
import { BrowserRouter, Route, Switch } from 'react-router-dom';
|
import { BrowserRouter, Route, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import 'bootstrap/dist/css/bootstrap.css';
|
import 'bootstrap/dist/css/bootstrap.css';
|
||||||
import 'sour-is/src/sour-is/Framework.css';
|
import './paste/Framework.css';
|
||||||
|
|
||||||
import Paste from './vendor/sour-is/src/paste/Paste';
|
import Paste from './paste/Paste';
|
||||||
import { remoteService } from "./vendor/sour-is/src/sour-is/RemoteService";
|
import { remoteService } from "./paste/RemoteService";
|
||||||
|
|
||||||
const req = remoteService();
|
const req = remoteService();
|
||||||
const APP_NAME = "DN42 Paste UI";
|
const APP_NAME = "DN42 Paste UI";
|
||||||
|
|
70
assets/src/paste/Framework.css
Normal file
70
assets/src/paste/Framework.css
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
body,html {color: #c8c8c8; background-color: #272b30; }
|
||||||
|
header + section { padding-top: 52px; padding-bottom: 2.1em }
|
||||||
|
footer { background-color: #3e444c; position:fixed; bottom:0; width: 100%; height: 2em; border-top: 1px solid #bbb; padding: 4px }
|
||||||
|
footer span.left { float: left; }
|
||||||
|
footer span.right { float: right; }
|
||||||
|
|
||||||
|
.breadcrumb label { color: black; }
|
||||||
|
|
||||||
|
.sidebar .list-group {
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .list-group-item {
|
||||||
|
padding: 2px;
|
||||||
|
background:#111;
|
||||||
|
color: #62c462;
|
||||||
|
border:0;
|
||||||
|
font-family: Hack, monospace
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
font-family: Hack, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar li.list-group-item.active,.sidebar a.list-group-item.active {
|
||||||
|
color: #111;
|
||||||
|
background-color: #62c462;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.hide-sm { display: none !important; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
position:fixed;
|
||||||
|
top: 52px;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 0;
|
||||||
|
width: 24vw;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
position: fixed;
|
||||||
|
top: 52px;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 25vw;
|
||||||
|
right: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.sidebar {
|
||||||
|
position:fixed;
|
||||||
|
top: 52px;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 0;
|
||||||
|
width: 24vw;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.main {
|
||||||
|
position: fixed;
|
||||||
|
top: 52px;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 25vw;
|
||||||
|
right: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
400
assets/src/paste/Paste.js
Normal file
400
assets/src/paste/Paste.js
Normal file
|
@ -0,0 +1,400 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import { Form, Button, Alert } from "react-bootstrap";
|
||||||
|
|
||||||
|
import "./paste.css";
|
||||||
|
|
||||||
|
import "highlight.js/styles/github.css";
|
||||||
|
import Highlight from "react-highlight";
|
||||||
|
import Markdown from 'react-markdown';
|
||||||
|
|
||||||
|
import pako from "pako";
|
||||||
|
import seedrandom from "seedrandom";
|
||||||
|
import { remoteService } from "./RemoteService"
|
||||||
|
|
||||||
|
import RIPEMD160 from "crypto-js/ripemd160";
|
||||||
|
import SHA256 from "crypto-js/sha256";
|
||||||
|
import AES from "crypto-js/aes";
|
||||||
|
import BASE64 from "crypto-js/enc-base64";
|
||||||
|
import HEX from "crypto-js/enc-hex";
|
||||||
|
import UTF8 from "crypto-js/enc-utf8"
|
||||||
|
|
||||||
|
const rng = seedrandom();
|
||||||
|
const req = remoteService();
|
||||||
|
|
||||||
|
const b64 = (s) => (!!s?BASE64.stringify(s).replace(/[=]+/, '').replace(/\//g, '_').replace(/\+/g, '-'):'');
|
||||||
|
const sha = (s) => b64(SHA256(s));
|
||||||
|
const chk = (s) => b64(RIPEMD160(SHA256(s)));
|
||||||
|
const enc = (t, p) => AES.encrypt(t, p).toString();
|
||||||
|
const u16a = (ua) => {
|
||||||
|
let s = '';
|
||||||
|
for (let i = 0; i < ua.length; i++) {
|
||||||
|
s += ('0' + ua[i].toString(16)).slice(-2);
|
||||||
|
}
|
||||||
|
return HEX.parse(s);
|
||||||
|
};
|
||||||
|
const u8a = (wa) => {
|
||||||
|
const w = wa.words;
|
||||||
|
let b = new Uint8Array(w.length * 4), v, i, j, k = 0;
|
||||||
|
for (i = 0; i < w.length; ++i) {
|
||||||
|
v = w[i];
|
||||||
|
for (j = 3; j >= 0; --j) {
|
||||||
|
b[k++] = ((v >> 8 * j) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
const str8 = (ua) => {
|
||||||
|
let s = '';
|
||||||
|
for (let i = 0; i < ua.byteLength; i++) {
|
||||||
|
if ((ua[i]&0x80) === 0) s += String.fromCharCode(ua[i]);
|
||||||
|
else if ((ua[i]&0xe0) === 0xc0 && (ua[i+1]&0xc0) === 0x80) {
|
||||||
|
s += String.fromCharCode(((ua[i]&0x1f)<<6) + (ua[i+1]&0x3f));
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else if ((ua[i]&0xf0) === 0xe0 && (ua[i+1]&0xc0) === 0x80 && (ua[i+2]&0xc0) === 0x80){
|
||||||
|
s += String.fromCharCode(((ua[i]&0x0f)<<12) + ((ua[i+1]&0x3f)<<6) + (ua[i+2]&0x3f));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if ((ua[i]&0xf8) === 0xf0 && (ua[i+1]&0xc0) === 0x80 && (ua[i+2]&0xc0) === 0x80 && (ua[i+3]&0xc0) === 0x80) {
|
||||||
|
s += String.fromCharCode(((ua[i]&0x0f)<<18) + ((ua[i+1]&0x3f)<<12) + ((ua[i+2]&0x3f)<<6) + (ua[i+3]&0x3f));
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else { s += String.fromCharCode(65533); }
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
const zip = (text) => u16a(pako.gzip(text));
|
||||||
|
const dec = (c, p, z) => {
|
||||||
|
if (z) {
|
||||||
|
let tx = AES.decrypt(c, p);
|
||||||
|
tx = u8a(tx);
|
||||||
|
return str8(pako.inflate(tx));
|
||||||
|
} else {
|
||||||
|
return AES.decrypt(c, p).toString(UTF8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const blength = (o) => {
|
||||||
|
if (!(typeof o === 'string' || o instanceof String)) return 0;
|
||||||
|
if (o === undefined || o.length === undefined) return 0;
|
||||||
|
var utf8length = 0;
|
||||||
|
for (var n = 0; n < o.length; n++) {
|
||||||
|
var c = o.charCodeAt(n);
|
||||||
|
if (c < 128) {
|
||||||
|
utf8length++;
|
||||||
|
}
|
||||||
|
else if ((c > 127) && (c < 2048)) {
|
||||||
|
utf8length = utf8length + 2;
|
||||||
|
} else {
|
||||||
|
utf8length = utf8length + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return utf8length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PASTE_API = "https://paste.dn42.us/paste"
|
||||||
|
|
||||||
|
const syntaxItems = [
|
||||||
|
["text", "Plain Text"],
|
||||||
|
["apache", "Apache"],
|
||||||
|
["bash", "Bash"],
|
||||||
|
["coffeescript", "CoffeeScript"],
|
||||||
|
["cpp", "C++"],
|
||||||
|
["cs", "C#"],
|
||||||
|
["css", "CSS"],
|
||||||
|
["diff", "Diff"],
|
||||||
|
["http", "HTTP"],
|
||||||
|
["ini", "Ini"],
|
||||||
|
["java", "Java"],
|
||||||
|
["javascript", "JavaScript"],
|
||||||
|
["json", "JSON"],
|
||||||
|
["makefile", "Makefile"],
|
||||||
|
["markdown", "Markdown"],
|
||||||
|
["nginx", "Nginx"],
|
||||||
|
["objectivec", "Objective C"],
|
||||||
|
["perl", "Perl"],
|
||||||
|
["php", "PHP"],
|
||||||
|
["python", "Python"],
|
||||||
|
["ruby", "Ruby"],
|
||||||
|
["sql", "SQL"],
|
||||||
|
["xml", "HTML, XML"]
|
||||||
|
];
|
||||||
|
const expireItems = [
|
||||||
|
[3600, "1 Hour"],
|
||||||
|
[86400, "1 Day"],
|
||||||
|
[604800, "1 Week"],
|
||||||
|
[2419200, "4 Weeks"],
|
||||||
|
[15778463, "6 Months"],
|
||||||
|
[31556926, "1 Year"]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
class Paste extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { location } = props;
|
||||||
|
|
||||||
|
const [ hash, key ] = location.hash.substring(2).split("!");
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
error: "",
|
||||||
|
syntax: "text",
|
||||||
|
expire: 604800,
|
||||||
|
expires: "",
|
||||||
|
burn: false,
|
||||||
|
plain: "",
|
||||||
|
cipher: "",
|
||||||
|
decryptKey: hash !== "new" ? key : "",
|
||||||
|
hash: hash !== "new" ? hash : "",
|
||||||
|
entropy: 0,
|
||||||
|
gzip: false,
|
||||||
|
syntaxItems,
|
||||||
|
expireItems
|
||||||
|
};
|
||||||
|
|
||||||
|
this.startEntropy = this.startEntropy.bind(this);
|
||||||
|
this.addEntropy = this.addEntropy.bind(this);
|
||||||
|
this.onChange = this.onChange.bind(this);
|
||||||
|
this.onSubmit = this.onSubmit.bind(this);
|
||||||
|
this.onNew = this.onNew.bind(this);
|
||||||
|
this.onCopy = this.onCopy.bind(this);
|
||||||
|
this.encrypt = this.encrypt.bind(this);
|
||||||
|
this.decrypt = this.decrypt.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
startEntropy(events, count) {
|
||||||
|
let t = [];
|
||||||
|
|
||||||
|
let fn = (e) => {
|
||||||
|
t.push([e.pageX, e.pageY, e.keyCode, +new Date()]);
|
||||||
|
if (t.length < count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.addEntropy(t);
|
||||||
|
t = [];
|
||||||
|
};
|
||||||
|
fn = fn.bind(this);
|
||||||
|
|
||||||
|
for (let i in events) {
|
||||||
|
if (events.hasOwnProperty(i))
|
||||||
|
document.addEventListener(events[i], fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addEntropy(s) {
|
||||||
|
this.setState(function(state, props) {
|
||||||
|
return {entropy: state.entropy + s.length};
|
||||||
|
} );
|
||||||
|
Math.seedrandom(s, {entropy: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
onChange(event) {
|
||||||
|
const target = event.target;
|
||||||
|
const value = target.type === 'checkbox' ? target.checked : target.value;
|
||||||
|
const name = target.name;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
[name]: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const { plain } = this.state;
|
||||||
|
const { history } = this.props;
|
||||||
|
|
||||||
|
this.encrypt(plain).then(([hash, decryptKey]) => { history.push('/paste/#/' + hash + '!' + decryptKey) });
|
||||||
|
}
|
||||||
|
onNew(event) {
|
||||||
|
const { history } = this.props;
|
||||||
|
this.setState({cipher:"", plain:"", hash: "", decryptKey: "", syntax: "text", expire: 604800, burn: false}, () => history.push('/paste'));
|
||||||
|
}
|
||||||
|
onCopy(event) {
|
||||||
|
const { history } = this.props;
|
||||||
|
this.setState({hash: "", decryptKey: ""}, () => history.push('/paste'))
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypt(tx) {
|
||||||
|
if (tx === "") {
|
||||||
|
this.setState({hash: "", decryptKey: "", error: "Unable to retrieve paste."})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = tx.split('\n');
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
let header = {};
|
||||||
|
while (true) {
|
||||||
|
if (s[i] === "") break;
|
||||||
|
|
||||||
|
var l = s[i].trim().split(':\t');
|
||||||
|
header[l[0]] = l[1];
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
const cipher = s.splice(i).join('');
|
||||||
|
const zip = !!header.zip && header.zip === "true";
|
||||||
|
|
||||||
|
const { decryptKey } = this.state;
|
||||||
|
const plain = dec(cipher, decryptKey, zip);
|
||||||
|
const expires = !!header.exp ? (!!header.burn ? "Burn on Read" : ((d) => d.toLocaleDateString() + " " + d.toLocaleTimeString())(new Date(header.exp*1000))) : "Never";
|
||||||
|
this.setState({cipher: tx, plain: plain, expires, syntax: header.lang});
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypt(input) {
|
||||||
|
const rnd = rng(40);
|
||||||
|
const decryptKey = sha(rnd);
|
||||||
|
|
||||||
|
const gzip = blength(input)>4000;
|
||||||
|
const plain = gzip?zip(input):input;
|
||||||
|
|
||||||
|
const { syntax, expire, burn } = this.state;
|
||||||
|
|
||||||
|
const header = {
|
||||||
|
chk: chk(rnd),
|
||||||
|
lang: syntax,
|
||||||
|
exp: parseInt(expire, 10) + (Date.now() / 1000 | 0),
|
||||||
|
zip: gzip,
|
||||||
|
burn: burn
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = '', e = enc(plain, decryptKey);
|
||||||
|
while (e.length > 79) {
|
||||||
|
s += e.slice(0, 79) + "\n";
|
||||||
|
e = e.slice(79);
|
||||||
|
}
|
||||||
|
s += e + "\n";
|
||||||
|
|
||||||
|
const expires = !!header.exp ? (!!header.burn ? "Burn on Read" : ((d) => d.toLocaleDateString() + " " + d.toLocaleTimeString())(new Date(header.exp*1000))) : "Never";
|
||||||
|
const cipher = Object.entries(header).map(([k, v]) => !!v ? k + ":\t" + v + "\n" : "").join('') + "\n" + s;
|
||||||
|
|
||||||
|
return req(PASTE_API).post({}, cipher)
|
||||||
|
.then((r) => r.text())
|
||||||
|
.then((d) => {
|
||||||
|
console.log("Received:\n" + d);
|
||||||
|
const [ok='', hash=''] = d.split(' ', 2);
|
||||||
|
if (ok === "OK")
|
||||||
|
this.setState((state) => ({cipher, gzip, decryptKey, hash, expires}));
|
||||||
|
else console.log(d);
|
||||||
|
return [hash, decryptKey];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.startEntropy(['mousemove', 'keydown', 'keypress', 'click', 'scroll'], 16);
|
||||||
|
req(`${PASTE_API}/rng`).get().then((res)=>res.text()).then(this.addEntropy).catch();
|
||||||
|
|
||||||
|
const { hash } = this.state;
|
||||||
|
if (hash !== "")
|
||||||
|
req(`${PASTE_API}/${hash}`).get().then((res)=> res.ok ? res.text() : "").then(this.decrypt).catch();
|
||||||
|
}
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { location } = nextProps;
|
||||||
|
const { hash:nextHash } = location;
|
||||||
|
const [ hash='', key='' ] = nextHash.substring(2).split("!");
|
||||||
|
|
||||||
|
if (hash === this.state.hash) return;
|
||||||
|
|
||||||
|
if (hash === '') this.setState({cipher:"", plain:"", hash: "", decryptKey: "", syntax: "text", expire: 604800, burn: false})
|
||||||
|
else {
|
||||||
|
this.setState({hash: hash, decryptKey: key});
|
||||||
|
req(`${PASTE_API}/${hash}`).get().then((res)=> res.ok ? res.text() : "").then(this.decrypt).catch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { hash } = this.state;
|
||||||
|
return hash === '' ? <PasteCreate {...this.state} onChange={this.onChange} onSubmit={this.onSubmit}/> : <PasteView {...this.state} onNew={this.onNew} onCopy={this.onCopy}/> ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function PasteCreate({error, onSubmit, onChange, syntax, syntaxItems, expire, expireItems, burn, entropy, plain}) {
|
||||||
|
return (
|
||||||
|
<section className="container">
|
||||||
|
<div>
|
||||||
|
{!!error ?
|
||||||
|
<Alert bsStyle="warning">
|
||||||
|
<strong>Holy guacamole!</strong> {error}
|
||||||
|
</Alert> : ""}
|
||||||
|
|
||||||
|
<Form name='paste' onSubmit={onSubmit}>
|
||||||
|
<div className="form form-inline">
|
||||||
|
<ol className='breadcrumb'>
|
||||||
|
<li>
|
||||||
|
<label>Syntax</label>
|
||||||
|
<select className='form-control input-sm' name="syntax" onChange={onChange} value={syntax}>
|
||||||
|
{syntaxItems.map((o) => <option key={o[0]} value={o[0]}>{o[1]}</option>)}
|
||||||
|
</select>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>Expires</label>
|
||||||
|
<select className='form-control input-sm' name="expire" onChange={onChange} value={expire}>
|
||||||
|
{expireItems.map((o) => <option key={o[0]} value={o[0]}>{o[1]}</option>)}
|
||||||
|
</select>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label><input type='checkbox' name="burn" onChange={onChange} value={burn}/> Burn on Read</label>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea required className='form-control' rows='20' name="plain" onChange={onChange} value={plain}></textarea>
|
||||||
|
<pre>Additional Entropy: {entropy} bytes / Content size: {blength(plain)} bytes</pre>
|
||||||
|
|
||||||
|
<Button type='submit' className='btn btn-default btn-lg btn-block'>Encrypt</Button>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
<p>Create pastes from the command line! <a href='./paste.sh' download>paste.sh</a></p>
|
||||||
|
|
||||||
|
<pre>{`$ echo /etc/passwd | ./paste.sh
|
||||||
|
|
||||||
|
env options:
|
||||||
|
PASTE_URL - Set the url base for paste operations (default: HTTPS://paste.dn42.us)
|
||||||
|
PASTE_GZIP - 0 = No Compression, 1 = Use gzip compression (default: 0)
|
||||||
|
PASTE_BURN - 0 = No Burn on Read, 1 = Burn on read (default: 0)
|
||||||
|
PASTE_DATE - Value to be used when setting expire date. (default: next-week)`}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PasteView({hash, decryptKey, expires, gzip, cipher, plain, syntax, onNew, onCopy}) {
|
||||||
|
const gzipOpts = gzip ? '| gzip -dc' : '';
|
||||||
|
return (
|
||||||
|
<section className="container">
|
||||||
|
<div className="input-group">
|
||||||
|
<span className="input-group-btn">
|
||||||
|
<a className="btn btn-default" type="button" onClick={onNew}>New</a>
|
||||||
|
</span>
|
||||||
|
<input type='text' readOnly className='form-control' value={`${window.location.origin}/paste/#/${hash}!${decryptKey}`} onClick={(e) => e.target.select()}/>
|
||||||
|
<span className="input-group-btn">
|
||||||
|
<a className="btn btn-default" type="button" onClick={onCopy}>Copy</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="store.err == undefined">
|
||||||
|
<div className='well well-sm'>
|
||||||
|
<b>Lang:</b> {syntax}, <b>Expires:</b> {expires}
|
||||||
|
</div>
|
||||||
|
{syntax==="markdown" ? (
|
||||||
|
<Markdown source={plain} />
|
||||||
|
) : (
|
||||||
|
<Highlight className={syntax}>{plain}</Highlight>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
<pre>{`# Command Line:
|
||||||
|
curl -s "${PASTE_API}/${hash}" \\
|
||||||
|
| sed "1,/^\\$/d" \\
|
||||||
|
| openssl aes-256-cbc -md md5 \\
|
||||||
|
-d -a -k "${decryptKey}" ${gzipOpts}
|
||||||
|
|
||||||
|
${cipher}`}</pre>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Paste;
|
88
assets/src/paste/RemoteService.js
Normal file
88
assets/src/paste/RemoteService.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
const uuid = () =>
|
||||||
|
([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
|
||||||
|
((c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & 15)) >> c / 4).toString(16))
|
||||||
|
|
||||||
|
|
||||||
|
let COUNTER = 0, s
|
||||||
|
if (localStorage.getItem("session-id") === null) {
|
||||||
|
s = [uuid(), 0]
|
||||||
|
} else {
|
||||||
|
s = localStorage.getItem("session-id").split(":");
|
||||||
|
s[1] = (parseInt(s[1], 16) + 1).toString(36)
|
||||||
|
}
|
||||||
|
const SESSION = s.join(":");
|
||||||
|
localStorage.setItem("session-id", SESSION);
|
||||||
|
|
||||||
|
export function remoteService() {
|
||||||
|
return function(url, fn, config) {
|
||||||
|
if (config === undefined) config = {};
|
||||||
|
if (config.headers === undefined) config.headers = {};
|
||||||
|
|
||||||
|
const execute = function (fn, method, url, query, payload, headers) {
|
||||||
|
if (query === undefined) query = {};
|
||||||
|
for (var i in query) if (query.hasOwnProperty(i)) {
|
||||||
|
var needle = ':'+i;
|
||||||
|
if (url.search(needle)>0) {
|
||||||
|
url = url.replace(needle, query[i]);
|
||||||
|
delete(query[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.method = method;
|
||||||
|
|
||||||
|
if (payload !== undefined)
|
||||||
|
if (typeof payload === 'string' || payload instanceof String)
|
||||||
|
config.body = payload;
|
||||||
|
else config.body = JSON.stringify(payload);
|
||||||
|
|
||||||
|
if (fn !== undefined) config = fn(config);
|
||||||
|
config.headers["session-id"] = SESSION+":"+(COUNTER++).toString(36)
|
||||||
|
|
||||||
|
return fetch(url, config);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
get: function (query, headers) { return execute(fn, 'GET', url, query, undefined, headers); },
|
||||||
|
put: function (query, payload, headers) { return execute(fn, 'PUT', url, query, payload, headers); },
|
||||||
|
post: function (query, payload, headers) { return execute(fn, 'POST', url, query, payload, headers); },
|
||||||
|
patch: function (query, payload, headers) { return execute(fn, 'PATCH', url, query, payload, headers); },
|
||||||
|
del: function (query, headers) { return execute(fn, 'DELETE', url, query, undefined, headers); },
|
||||||
|
'delete': function (query, headers) { return execute(fn, 'DELETE', url, query, undefined, headers); },
|
||||||
|
'remove': function (query, headers) { return execute(fn, 'DELETE', url, query, undefined, headers); },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sessionRemoteService() {
|
||||||
|
const req = remoteService();
|
||||||
|
|
||||||
|
return function(url) {
|
||||||
|
var fn = function(config) {
|
||||||
|
if (localStorage.getItem("session") !== null)
|
||||||
|
config.headers['authorization'] = "session " + localStorage.getItem("session");
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
return req(url, fn);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sourisRemoteService() {
|
||||||
|
const req = remoteService();
|
||||||
|
|
||||||
|
return function(url, aspect) {
|
||||||
|
if (aspect === undefined) aspect = 'default';
|
||||||
|
|
||||||
|
var fn = function(config) {
|
||||||
|
if (localStorage.getItem('souris_token') !== undefined)
|
||||||
|
config.headers['authorization'] = "souris " + aspect + " " + localStorage.getItem('souris_token');
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
return req(url, fn);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default remoteService;
|
102
assets/src/paste/paste.css
Normal file
102
assets/src/paste/paste.css
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
section.container { margin-bottom: 2em; }
|
||||||
|
textarea { font-family: "Fira Code",hack,"Anonymous Pro",monospace; }
|
||||||
|
pre { font-family: "Fira Code",hack,"Anonymous Pro",monospace; }
|
||||||
|
code { font-family: "Fira Code",hack,"Anonymous Pro",monospace; }
|
||||||
|
|
||||||
|
table a:link {
|
||||||
|
color: #666;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
table a:visited {
|
||||||
|
color: #999999;
|
||||||
|
font-weight:bold;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
table a:active,
|
||||||
|
table a:hover {
|
||||||
|
color: #bd5a35;
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
font-family:Arial, Helvetica, sans-serif;
|
||||||
|
color:#666;
|
||||||
|
font-size:12px;
|
||||||
|
text-shadow: 1px 1px 0px #fff;
|
||||||
|
background:#eaebec;
|
||||||
|
margin:20px;
|
||||||
|
border:#ccc 1px solid;
|
||||||
|
|
||||||
|
-moz-border-radius:3px;
|
||||||
|
-webkit-border-radius:3px;
|
||||||
|
border-radius:3px;
|
||||||
|
|
||||||
|
-moz-box-shadow: 0 1px 2px #d1d1d1;
|
||||||
|
-webkit-box-shadow: 0 1px 2px #d1d1d1;
|
||||||
|
box-shadow: 0 1px 2px #d1d1d1;
|
||||||
|
}
|
||||||
|
table th {
|
||||||
|
padding:21px 25px 22px 25px;
|
||||||
|
border-top:1px solid #fafafa;
|
||||||
|
border-bottom:1px solid #e0e0e0;
|
||||||
|
|
||||||
|
background: #ededed;
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#ebebeb));
|
||||||
|
background: -moz-linear-gradient(top, #ededed, #ebebeb);
|
||||||
|
}
|
||||||
|
table th:first-child {
|
||||||
|
text-align: left;
|
||||||
|
padding-left:20px;
|
||||||
|
}
|
||||||
|
table tr:first-child th:first-child {
|
||||||
|
-moz-border-radius-topleft:3px;
|
||||||
|
-webkit-border-top-left-radius:3px;
|
||||||
|
border-top-left-radius:3px;
|
||||||
|
}
|
||||||
|
table tr:first-child th:last-child {
|
||||||
|
-moz-border-radius-topright:3px;
|
||||||
|
-webkit-border-top-right-radius:3px;
|
||||||
|
border-top-right-radius:3px;
|
||||||
|
}
|
||||||
|
table tr {
|
||||||
|
text-align: center;
|
||||||
|
padding-left:20px;
|
||||||
|
}
|
||||||
|
table td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
padding-left:20px;
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
table td {
|
||||||
|
padding:18px;
|
||||||
|
border-top: 1px solid #ffffff;
|
||||||
|
border-bottom:1px solid #e0e0e0;
|
||||||
|
border-left: 1px solid #e0e0e0;
|
||||||
|
|
||||||
|
background: #fafafa;
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, from(#fbfbfb), to(#fafafa));
|
||||||
|
background: -moz-linear-gradient(top, #fbfbfb, #fafafa);
|
||||||
|
}
|
||||||
|
table tr.even td {
|
||||||
|
background: #f6f6f6;
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#f6f6f6));
|
||||||
|
background: -moz-linear-gradient(top, #f8f8f8, #f6f6f6);
|
||||||
|
}
|
||||||
|
table tr:last-child td {
|
||||||
|
border-bottom:0;
|
||||||
|
}
|
||||||
|
table tr:last-child td:first-child {
|
||||||
|
-moz-border-radius-bottomleft:3px;
|
||||||
|
-webkit-border-bottom-left-radius:3px;
|
||||||
|
border-bottom-left-radius:3px;
|
||||||
|
}
|
||||||
|
table tr:last-child td:last-child {
|
||||||
|
-moz-border-radius-bottomright:3px;
|
||||||
|
-webkit-border-bottom-right-radius:3px;
|
||||||
|
border-bottom-right-radius:3px;
|
||||||
|
}
|
||||||
|
table tr:hover td {
|
||||||
|
background: #f2f2f2;
|
||||||
|
background: -webkit-gradient(linear, left top, left bottom, from(#f2f2f2), to(#f0f0f0));
|
||||||
|
background: -moz-linear-gradient(top, #f2f2f2, #f0f0f0);
|
||||||
|
}
|
1
assets/src/vendor/sour-is
vendored
1
assets/src/vendor/sour-is
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit 12c317f57769905c4a10048f5caa49b3b8b7a299
|
|
3
go.mod
3
go.mod
|
@ -11,6 +11,7 @@ require (
|
||||||
github.com/sour-is/go-assetfs v1.0.0
|
github.com/sour-is/go-assetfs v1.0.0
|
||||||
github.com/spf13/viper v1.6.2
|
github.com/spf13/viper v1.6.2
|
||||||
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e
|
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20200817085935-3ff754bf58a9
|
||||||
sour.is/x/toolbox v0.11.4
|
sour.is/x/toolbox v0.11.4
|
||||||
)
|
)
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -366,7 +366,10 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||||
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||||
|
@ -413,6 +416,8 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200817085935-3ff754bf58a9 h1:MEU99+Z67sctTw1UjDlQ6wjRF77I43fOt7YKWktVvXw=
|
||||||
|
golang.org/x/sys v0.0.0-20200817085935-3ff754bf58a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
|
|
@ -8,4 +8,4 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate go run github.com/sour-is/go-assetfs/cmd/assetfs -pkg routes -prefix ../../ ../../public/ ../../public/static/css/ ../../public/static/js/ ../../public/static/media/
|
//go:generate go run github.com/sour-is/go-assetfs/cmd/assetfs -pkg routes -prefix ../../ ../../public/ ../../public/static/css/ ../../public/static/js/
|
||||||
|
|
Loading…
Reference in New Issue
Block a user