From bd838f52f57ffee922f25b8dfbb6e75700f85a11 Mon Sep 17 00:00:00 2001 From: Chloe Kudryavtsev Date: Tue, 26 Feb 2019 00:05:16 -0500 Subject: Initial draft version --- .gitignore | 15 +++++++++++++ LICENSE | 22 ++++++++++++++++++ dub.sdl | 6 +++++ dub.selections.json | 18 +++++++++++++++ source/app.d | 22 ++++++++++++++++++ source/hash.d | 13 +++++++++++ source/package.d | 3 +++ source/web.d | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ views/code.dt | 23 +++++++++++++++++++ views/index.dt | 37 +++++++++++++++++++++++++++++++ views/layout.dt | 27 ++++++++++++++++++++++ 11 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 dub.sdl create mode 100644 dub.selections.json create mode 100644 source/app.d create mode 100644 source/hash.d create mode 100644 source/package.d create mode 100644 source/web.d create mode 100644 views/code.dt create mode 100644 views/index.dt create mode 100644 views/layout.dt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2676d76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.dub +docs.json +__dummy.html +docs/ +/brpaste +brpaste.so +brpaste.dylib +brpaste.dll +brpaste.a +brpaste.lib +brpaste-test-* +*.exe +*.o +*.obj +*.lst diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fa84bf4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2019 Chloe Kudryavtsev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/dub.sdl b/dub.sdl new file mode 100644 index 0000000..8c32044 --- /dev/null +++ b/dub.sdl @@ -0,0 +1,6 @@ +name "brpaste" +description "A pastebin server so fast, it burns rubber." +authors "Chloe Kudryavtsev" +copyright "Copyright © 2019, Chloe Kudryavtsev" +license "MIT" +dependency "vibe-d" version="~>0.8.5-beta.2" diff --git a/dub.selections.json b/dub.selections.json new file mode 100644 index 0000000..a963d46 --- /dev/null +++ b/dub.selections.json @@ -0,0 +1,18 @@ +{ + "fileVersion": 1, + "versions": { + "botan": "1.12.10", + "botan-math": "1.0.3", + "diet-ng": "1.5.0", + "eventcore": "0.8.41", + "libasync": "0.8.3", + "libevent": "2.0.2+2.0.16", + "memutils": "0.4.13", + "mir-linux-kernel": "1.0.1", + "openssl": "1.1.6+1.0.1g", + "stdx-allocator": "2.77.5", + "taggedalgebraic": "0.11.3", + "vibe-core": "1.6.0", + "vibe-d": "0.8.5-beta.2" + } +} diff --git a/source/app.d b/source/app.d new file mode 100644 index 0000000..7b7c3fd --- /dev/null +++ b/source/app.d @@ -0,0 +1,22 @@ +import brpaste; + +import vibe.d; + +int main(string[] args) +{ + auto settings = new HTTPServerSettings; + settings.port = 8080; + settings.bindAddresses = []; + + readOption("bind|b", &settings.bindAddresses, "Sets the address to bind to"); + readOption("port|p", &settings.port, "Sets the port to listen on"); + if(settings.bindAddresses.length == 0) settings.bindAddresses = [ "127.0.0.1" ]; + + auto router = new URLRouter; + router.registerWebInterface(new BRPaste); + listenHTTP(settings, router); + + import std.conv; + logInfo("Please open http://%s:%d/ in your browser.", settings.bindAddresses[0], settings.port); + return runApplication(); +} diff --git a/source/hash.d b/source/hash.d new file mode 100644 index 0000000..3071fa3 --- /dev/null +++ b/source/hash.d @@ -0,0 +1,13 @@ +module brpaste.hash; + +pure string hash(T)(T data) { + import std.base64; + import std.digest.murmurhash; + auto hash = digest!(MurmurHash3!32)(data); + return Base64URLNoPadding.encode(hash); +} + +pure string hash(T : string)(T data) { + import std.string; + return hash(data.representation); +} diff --git a/source/package.d b/source/package.d new file mode 100644 index 0000000..cc9ed08 --- /dev/null +++ b/source/package.d @@ -0,0 +1,3 @@ +module brpaste; + +public import brpaste.web; diff --git a/source/web.d b/source/web.d new file mode 100644 index 0000000..ca3b644 --- /dev/null +++ b/source/web.d @@ -0,0 +1,64 @@ +module brpaste.web; + +import brpaste.hash; + +import vibe.vibe; + +RedisDatabase client; +shared static this() { + string path = "redis://127.0.0.1"; + readOption("redis|r", &path, "The URL to use to connect to redis"); + URL redis = path; + client = connectRedisDB(redis); +} + +class BRPaste { + @method(HTTPMethod.REPORT) + @path("/health") + void health(HTTPServerResponse res) { + import std.array; + res.statusCode = 200; + auto app = appender!string; + + // is Redis healthy? - FIXME: stack traces over HTTP are fun, I guess + import std.random; + long val = uniform!uint; + auto ech = client.client.echo!(long, long)(val); + if(val != ech) { + res.statusCode = 500; + app.put("Redis: failed."); + } else app.put("Redis: pass."); + + res.writeBody(app.data); + } + + void index() { + render!("index.dt"); + } + + @path("/:id") + void getId(string _id) { + if (!client.exists(_id)) throw new HTTPStatusException(404); + string language; + // TODO: rewrite the next two lines once #2273 is resolved + auto req = request; + if (req.query.length > 0) language = req.query.byKey.front; + auto data = client.get(_id); + render!("code.dt", data, language); + } + + @path("/raw/:id") + void getRawId(HTTPServerResponse res, string _id) { + if (!client.exists(_id)) throw new HTTPStatusException(404); + auto val = client.get(_id); + res.contentType = "text/plain"; + res.writeBody(val); + } + + void post(string data) { + auto hash = data.hash; + client.set(hash, data); + status(201); + response.writeBody(hash); + } +} diff --git a/views/code.dt b/views/code.dt new file mode 100644 index 0000000..255af74 --- /dev/null +++ b/views/code.dt @@ -0,0 +1,23 @@ +extends layout + +block css +block scripts + - string prefix = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0"; + link(rel='stylesheet', crossorigin='anonymous', + href='#{prefix}/themes/prism.min.css') +block bodyscripts + script( + src='#{prefix}/prism.min.js') + - import std.array; + - if(!language.empty) + script( + src='#{prefix}/components/prism-#{language}.min.js') + +block content + pre + - import std.array; + - string lang = "none"; + - if (!language.empty) lang = language; + code(class='language-#{lang}')= data + +//- vim: ft=pug diff --git a/views/index.dt b/views/index.dt new file mode 100644 index 0000000..24136ff --- /dev/null +++ b/views/index.dt @@ -0,0 +1,37 @@ +extends layout + +block content + h1 Burning Rubber Paste + h2 Usage + table(border='1') + thead: tr + th Method - Endpoint + th Effect + tbody + tr + td: pre: code POST /data=anything + td Pastebin anything + tr + td: pre: code GET /id + td Read paste with ID "id" + tr + td: pre: code GET /id?lang + td Read paste with ID "id", and highlight it as "lang" + tr + td: pre: code GET /raw/id + td Get the raw contents of paste with ID "id" + h2 Examples + pre: code(class='language-sh') + | curl -X POST -F 'sprunge=<-' https://brpaste.example.com + | http -f POST https://brpaste.example.com data=@file.txt + | curl https://brpaste.example.com/raw/some_id + | xdg-open https://brpaste.example.com/some_id?cpp + h2 Paste from a browser + form(action='/', method='post') + textarea(name='data', autocomplete='off', + required, autofocus, + cols='80', rows='27') + br + button(type='submit') Paste it! + +//- vim: ft=pug diff --git a/views/layout.dt b/views/layout.dt new file mode 100644 index 0000000..6b213a6 --- /dev/null +++ b/views/layout.dt @@ -0,0 +1,27 @@ +doctype html +html(lang='en') + head + meta(charset='utf-8') + meta(name='viewport', content='width=device-width, initial-scale=1') + block css + :css + body { + margin: 40px auto; + max-width: 650px; + line-height: 1.6; + font-size: 18px; + color: #444; + padding: 0 10px + } + h1,h2,h3 { line-height:1.2; } + td { text-align: left; } + block scripts + block title + title Burning Rubber Paste + body + #main + block content + p I'm not sure how you got here, but you shouldn't be here. + block bodyscripts + +//- vim: ft=pug -- cgit v1.2.3