Initial commit

pull/5/head
Cesura 3 years ago
commit 16a6747686

@ -0,0 +1,8 @@
FROM continuumio/conda-ci-linux-64-python3.8
USER root
COPY . /app/
RUN conda install -c anaconda tensorflow=2.2.0 pip
RUN pip install -r /app/requirements.txt
EXPOSE 5000
WORKDIR /app
ENTRYPOINT ["python3","app.py"]

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, Cesura
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,66 @@
![logo](https://i.imgur.com/W22RFJZ.png)
A lightweight, self-hosted paste platform
# Features
* Self-contained system without external database dependencies
* Automatic programming language detection
* Optional on-disk encryption
* Optional single use pastes
* IP/network whitelisting and blocking
* Endpoint rate limiting
* Fully configurable via environment variables
* Included script for uploading files/content from stdin
## Screenshots
### Browser
![screenshot1](https://i.imgur.com/rQ5ZIg7.png)
![screenshot2](https://i.imgur.com/zaDpBaX.png)
![screenshot3](https://i.imgur.com/XDlJDZS.png)
### CLI
![screenshot4](https://i.imgur.com/FFWGe43.png)
# Installation
### Docker
It is highly recommended that you use the official Docker image to run Pastey. To do so, simply run:
```
$ docker run -d -p 5000:5000 -v /path/to/local/dir:/app/data cesura/pastey:latest
```
Change **/path/to/local/dir** to a local folder you would like to use for persistent paste storage. It will be mounted in the container at **/app/data**.
Pastey will then be accessable at *http://localhost:5000*
### Local
Requirements:
* Python 3.8
* AVX-enabled processor (or a Python environment configured to use Anaconda's TensorFlow)
```
$ git clone https://github.com/Cesura/pastey.git && cd pastey
$ pip3 install -r requirements.txt
$ python3 app.py
```
# Configuration
Here is a list of the available configuration options:
| Environment Variable | config.py Variable | Description | Default Value |
|-----------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
| PASTEY_DATA_DIRECTORY | data_directory | Local directory for paste storage | ./data |
| PASTEY_LISTEN_ADDRESS | listen_address | Address to listen on | 0.0.0.0 |
| PASTEY_LISTEN_PORT | listen_port | Port to listen on | 5000 |
| PASTEY_USE_WHITELIST | use_whitelist | Enable/disable whitelisting for admin tasks (view recent, delete, config) | True |
| PASTEY_WHITELIST_CIDR | whitelist_cidr | List of whitelisted IP addresses or networks (in CIDR format). When passed as an environment variable, it should be a comma-separated list. | [ '127.0.0.1/32' , '10.0.0.0/8' , '172.16.0.0/12' , '192.168.0.0/16' ] |
| PASTEY_BLACKLIST_CIDR | blacklist_cidr | List of blocked IP addresses or networks (in CIDR format). When passed as an environment variable, it should be a comma-separated list. | [] |
| PASTEY_RESTRICT_PASTING | restrict_pasting | Enable/disable restricting of pasting to whitelisted users | False |
| PASTEY_RESTRICT_RAW_PASTING | restrict_raw_pasting | Enable/disable restricting of pasting via /raw to whitelisted users | True |
| PASTEY_RATE_LIMIT | rate_limit | Rate limit for pasting, for non-whitelisted users | 5/hour |
| PASTEY_GUESS_THRESHOLD | guess_threshold | Threshold for automatic language detection guesses. If a result is below this value, it is treated as Plaintext. | 0.20 |
| PASTEY_RECENT_PASTES | recent_pastes | Number of recent pastes to show on the home page | 10 |
| PASTEY_BEHIND_PROXY | behind_proxy | Inform Pastey if it is behind a reverse proxy (nginx, etc.). If this is the case, it will rely on HTTP headers X-Real-IP or X-Forwarded-For. NOTE: Make sure your proxy config sets these values | False |
### Docker configuration
For Docker environments, it is recommended that the options be passed to the container on startup:
```
$ docker run -d -p 5000:5000 -e PASTEY_LISTEN_PORT=80 -e PASTEY_BEHIND_PROXY="True" cesura/pastey:latest
```

323
app.py

@ -0,0 +1,323 @@
from flask import Flask, render_template, request, redirect, abort
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from guesslang import Guess
from datetime import datetime
from urllib.parse import quote
from cryptography.fernet import Fernet
import ipaddress
import uuid
import json
from os import path, remove, environ
from pathlib import Path
from config import config
from distutils.util import strtobool
app = Flask(__name__)
limiter = Limiter(
app,
key_func=get_remote_address
)
guess = Guess()
# Pastey version
pastey_version = "0.2"
loaded_config = {}
# Check environment variable overrides
config.data_directory = environ["PASTEY_DATA_DIRECTORY"] if "PASTEY_DATA_DIRECTORY" in environ else config.data_directory
config.listen_address = environ["PASTEY_LISTEN_ADDRESS"] if "PASTEY_LISTEN_ADDRESS" in environ else config.listen_address
config.listen_port = environ["PASTEY_LISTEN_PORT"] if "PASTEY_LISTEN_PORT" in environ else config.listen_port
config.use_whitelist = bool(strtobool(environ["PASTEY_USE_WHITELIST"])) if "PASTEY_USE_WHITELIST" in environ else config.use_whitelist
config.restrict_pasting = bool(strtobool(environ["PASTEY_RESTRICT_PASTING"])) if "PASTEY_RESTRICT_PASTING" in environ else config.restrict_pasting
config.restrict_raw_pasting = bool(strtobool(environ["PASTEY_RESTRICT_RAW_PASTING"])) if "PASTEY_RESTRICT_RAW_PASTING" in environ else config.restrict_raw_pasting
config.rate_limit = environ["PASTEY_RATE_LIMIT"] if "PASTEY_RATE_LIMIT" in environ else config.rate_limit
config.guess_threshold = float(environ["PASTEY_GUESS_THRESHOLD"]) if "PASTEY_GUESS_THRESHOLD" in environ else config.guess_threshold
config.recent_pastes = int(environ["PASTEY_RECENT_PASTES"]) if "PASTEY_RECENT_PASTES" in environ else config.recent_pastes
config.whitelist_cidr = environ["PASTEY_WHITELIST_CIDR"].split(",") if "PASTEY_WHITELIST_CIDR" in environ else config.whitelist_cidr
config.blacklist_cidr = environ["PASTEY_BLACKLIST_CIDR"].split(",") if "PASTEY_BLACKLIST_CIDR" in environ else config.blacklist_cidr
config.behind_proxy = bool(strtobool(environ["PASTEY_BEHIND_PROXY"])) if "PASTEY_BEHIND_PROXY" in environ else config.behind_proxy
# Check request IP is in config whitelist
def verify_whitelist(ip):
address = ipaddress.ip_address(ip)
# Check blacklist
for network in config.blacklist_cidr:
if address in ipaddress.IPv4Network(network):
return False
if not config.use_whitelist:
return True
# Check whitelist
for network in config.whitelist_cidr:
if address in ipaddress.IPv4Network(network):
return True
return False
# Solve anomalies in icon naming
def get_icon(language):
if language == "C#":
return "csharp"
elif language == "C++":
return "cplusplus"
elif language == "Jupyter Notebook":
return "jupyter"
else:
return language.lower()
# For handling reverse proxy configurations
# Note that these are HTTP headers and are generally untrustworthy
# Make sure your proxy configuration is either setting or clearing these
def get_source_ip(request):
if config.behind_proxy:
if 'X-Real-IP' in request.headers:
return request.headers['X-Real-IP']
elif 'X-Forwarded-For' in request.headers:
return request.headers['X-Forwarded-For']
return request.remote_addr
########## Paste functions ##########
# Get recent n pastes, defined in config by recent_pastes
def get_recent(limit=config.recent_pastes):
paths = sorted(Path(config.data_directory).iterdir(), key=path.getmtime, reverse=True)
recent_pastes = []
i = 0
while i < limit and i < len(paths):
with open(paths[i]) as fp:
paste = json.loads(fp.read())
paste['unique_id'] = path.basename(paths[i])
paste['content'] = '\n'.join(paste['content'].splitlines()[0:10])
paste['icon'] = get_icon(paste['language'])
if paste['encrypted']:
paste['content'] = "[Encrypted]"
recent_pastes.append(paste)
i += 1
return recent_pastes
# Get paste by ID
def get_paste(unique_id, key=""):
if path.exists(config.data_directory + "/" + unique_id):
with open(config.data_directory + "/" + unique_id, "r") as fp:
paste = json.loads(fp.read())
# Check remaining uses, and decrement
# -1 = unlimited uses
if paste['uses'] != -1:
paste['uses'] -= 1
if paste['uses'] == 0:
delete_paste(unique_id)
else:
with open(config.data_directory + "/" + unique_id, "w") as fp:
fp.write(json.dumps(paste))
# Decrypt content, if necessary
try:
if key != "":
cipher_suite = Fernet(key.encode('utf-8'))
paste['content'] = cipher_suite.decrypt(paste['content'].encode('utf-8')).decode('utf-8')
except Exception as e:
return 401
return paste
else:
return None
# Delete paste by ID
def delete_paste(unique_id):
paste = config.data_directory + "/" + unique_id
if path.exists(paste):
remove(paste)
# Create new paste
def new_paste(title, content, source_ip, single=False, encrypt=False):
unique_id = str(uuid.uuid4())
while path.exists(config.data_directory + "/" + unique_id):
unique_id = str(uuid.uuid4())
# Attempt to guess programming language
guesses = guess.probabilities(content)
language = guesses[0][0] if guesses[0][1] > config.guess_threshold and guesses[0][0] != "SQL" else "Plaintext"
# Check if encryption is necessary
key = ""
if encrypt:
init_key = Fernet.generate_key()
cipher_suite = Fernet(init_key)
content = cipher_suite.encrypt(content.encode('utf-8')).decode('utf-8')
key = init_key.decode('utf-8')
# Check if single use is set
uses = 2 if single else -1
output = {
"timestamp": datetime.now().strftime("%a, %d %b %Y at %H:%M:%S"),
"language": language,
"source_ip": source_ip,
"title": title,
"content": content,
"encrypted": encrypt,
"uses": uses
}
# Write to output file
with open(config.data_directory + "/" + unique_id, "w+") as fp:
fp.write(json.dumps(output))
return unique_id, key
########## Routes ##########
# Home page
@app.route("/")
def home():
whitelisted = verify_whitelist(get_source_ip(request))
pastes = []
if whitelisted:
pastes=get_recent()
return render_template("index.html", pastes=pastes, whitelisted=whitelisted)
# New paste page
@app.route("/new")
def new():
whitelisted = verify_whitelist(get_source_ip(request))
return render_template("new.html", whitelisted=whitelisted)
# Config page
@app.route("/config")
def config_page():
whitelisted = verify_whitelist(get_source_ip(request))
if not whitelisted:
abort(401)
return render_template("config.html", config_items=loaded_config,
script_url=request.url.rsplit('/', 1)[0] + "/pastey", whitelisted=whitelisted)
# View paste page
@app.route("/view/<unique_id>")
def view(unique_id):
whitelisted = verify_whitelist(get_source_ip(request))
content = get_paste(unique_id)
if content is not None:
return render_template("view.html", paste=content, url=request.url, whitelisted=whitelisted)
else:
abort(404)
# View paste page (encrypted)
@app.route("/view/<unique_id>/<key>")
def view_key(unique_id, key):
whitelisted = verify_whitelist(get_source_ip(request))
content = get_paste(unique_id, key=key)
if content == 401:
abort(401)
elif content is not None:
return render_template("view.html", paste=content, url=request.url, whitelisted=whitelisted)
else:
abort(404)
# Delete paste
@app.route("/delete/<unique_id>")
def delete(unique_id):
if not verify_whitelist(get_source_ip(request)):
abort(401)
delete_paste(unique_id)
return redirect("/")
@app.route("/pastey")
def pastey_script():
return render_template('pastey.sh', endpoint=request.url.rsplit('/', 1)[0] + "/raw"), 200, {
'Content-Disposition': 'attachment; filename="pastey"',
'Content-Type': 'text/plain'
}
# POST new paste
@app.route('/paste', methods = ['POST'])
@limiter.limit(config.rate_limit, exempt_when=lambda: verify_whitelist(get_source_ip(request)))
def paste():
source_ip = get_source_ip(request)
# Check if restrict pasting to whitelist CIDRs is enabled
if config.restrict_pasting and not verify_whitelist(source_ip):
abort(401)
content = request.form['content']
# Check if content is empty
if request.form['content'].strip() == "":
return redirect("/new")
else:
# Verify form options
title = request.form['title'] if request.form['title'].strip() != "" else "Untitled"
single = True if 'single' in request.form else False
encrypt = True if 'encrypt' in request.form else False
# Create paste
unique_id, key = new_paste(title, content, source_ip, single=single, encrypt=encrypt)
if encrypt:
return redirect("/view/" + unique_id + "/" + quote(key))
else:
return redirect("/view/" + unique_id)
# POST new raw paste
@app.route('/raw', methods = ['POST'])
@limiter.limit(config.rate_limit, exempt_when=lambda: verify_whitelist(get_source_ip(request)))
def raw():
source_ip = get_source_ip(request)
# Check if restrict pasting to whitelist CIDRs is enabled
if config.restrict_raw_pasting and not verify_whitelist(source_ip):
abort(401)
# Create paste
unique_id, key = new_paste("Untitled", request.data.decode('utf-8'), source_ip, single=False, encrypt=False)
link = request.url.rsplit('/', 1)[0] + "/view/" + unique_id
return link, 200
# Custom 404 handler
@app.errorhandler(404)
def page_not_found(e):
whitelisted = verify_whitelist(get_source_ip(request))
return render_template('404.html', whitelisted=whitelisted), 404
# Custom 401 handler
@app.errorhandler(401)
def unauthorized(e):
whitelisted = verify_whitelist(get_source_ip(request))
return render_template('401.html', whitelisted=whitelisted), 401
# Main loop
if __name__ == "__main__":
# Print configuration
print("=====================================")
print("Pastey version ", pastey_version)
print("USING THE FOLLOWING CONFIGURATION:")
print("=====================================")
for option in dir(config):
if not option.startswith("__"):
loaded_config[option] = eval("config.%s" % option)
print(option, ": ", loaded_config[option])
print("=====================================")
# Register error handlers
app.register_error_handler(404, page_not_found)
app.register_error_handler(401, unauthorized)
app.run(host=config.listen_address, port=config.listen_port)

@ -0,0 +1,37 @@
# Data directory
data_directory = "./data"
# Listen address
listen_address = "0.0.0.0"
# Listen port
listen_port = 5000
# Use whitelisting
# Whitelisted IPs can view recent pastes on the home page, as well as delete pastes
# For limiting pasting to whitelisted users, enable the "restrict_pasting" option below
use_whitelist = True
# Whitelist CIDR
whitelist_cidr = ['127.0.0.1/32', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
# Blacklist CIDR
blacklist_cidr = []
# Restrict pasting functionality to whitelisted IPs
restrict_pasting = False
# Restrict raw pasting to whitelisted IPs
restrict_raw_pasting = True
# Rate limit for pasting (ignored for whitelisted users)
rate_limit = "5/hour"
# Guess threshold for automatic language detection
guess_threshold = 0.20
# Number of recent pastes to show on the home page
recent_pastes = 10
# Try to use X-Real-IP or X-Forwarded-For HTTP headers
behind_proxy = False

@ -0,0 +1,4 @@
flask
Flask-Limiter==1.4
guesslang
cryptography

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:700}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:700}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:700}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

@ -0,0 +1,18 @@
.prettyprint ol.linenums > li { list-style-type: decimal; }
#intro {
margin-top: 58px;
}
@media (max-width: 991px) {
#intro {
margin-top: 45px;
}
}
.paste-checkbox {
padding-bottom: 20px;
}
.error {
font-size: 60px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

@ -0,0 +1,6 @@
function copyToClipboard() {
var copyText = document.getElementById("paste-url");
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,34 @@
!function(){var r=null;
(function(){function X(e){function j(){try{J.doScroll("left")}catch(e){P(j,50);return}w("poll")}function w(j){if(!(j.type=="readystatechange"&&x.readyState!="complete")&&((j.type=="load"?n:x)[z](i+j.type,w,!1),!m&&(m=!0)))e.call(n,j.type||j)}var Y=x.addEventListener,m=!1,C=!0,t=Y?"addEventListener":"attachEvent",z=Y?"removeEventListener":"detachEvent",i=Y?"":"on";if(x.readyState=="complete")e.call(n,"lazy");else{if(x.createEventObject&&J.doScroll){try{C=!n.frameElement}catch(A){}C&&j()}x[t](i+"DOMContentLoaded",
w,!1);x[t](i+"readystatechange",w,!1);n[t](i+"load",w,!1)}}function Q(){S&&X(function(){var e=K.length;$(e?function(){for(var j=0;j<e;++j)(function(e){P(function(){n.exports[K[e]].apply(n,arguments)},0)})(j)}:void 0)})}for(var n=window,P=n.setTimeout,x=document,J=x.documentElement,L=x.head||x.getElementsByTagName("head")[0]||J,z="",A=x.scripts,m=A.length;--m>=0;){var M=A[m],T=M.src.match(/^[^#?]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(T){z=T[1]||"";M.parentNode.removeChild(M);break}}var S=!0,D=
[],N=[],K=[];z.replace(/[&?]([^&=]+)=([^&]+)/g,function(e,j,w){w=decodeURIComponent(w);j=decodeURIComponent(j);j=="autorun"?S=!/^[0fn]/i.test(w):j=="lang"?D.push(w):j=="skin"?N.push(w):j=="callback"&&K.push(w)});m=0;for(z=D.length;m<z;++m)(function(){var e=x.createElement("script");e.onload=e.onerror=e.onreadystatechange=function(){if(e&&(!e.readyState||/loaded|complete/.test(e.readyState)))e.onerror=e.onload=e.onreadystatechange=r,--R,R||P(Q,0),e.parentNode&&e.parentNode.removeChild(e),e=r};e.type=
"text/javascript";e.src="https://google-code-prettify.googlecode.com/svn/loader/lang-"+encodeURIComponent(D[m])+".js";L.insertBefore(e,L.firstChild)})(D[m]);for(var R=D.length,A=[],m=0,z=N.length;m<z;++m)A.push("https://google-code-prettify.googlecode.com/svn/loader/skins/"+encodeURIComponent(N[m])+".css");A.push("https://google-code-prettify.googlecode.com/svn/loader/prettify.css");(function(e){function j(m){if(m!==w){var n=x.createElement("link");n.rel="stylesheet";n.type="text/css";if(m+1<w)n.error=
n.onerror=function(){j(m+1)};n.href=e[m];L.appendChild(n)}}var w=e.length;j(0)})(A);var $=function(){window.PR_SHOULD_USE_CONTINUATION=!0;var e;(function(){function j(a){function d(f){var b=f.charCodeAt(0);if(b!==92)return b;var a=f.charAt(1);return(b=i[a])?b:"0"<=a&&a<="7"?parseInt(f.substring(1),8):a==="u"||a==="x"?parseInt(f.substring(2),16):f.charCodeAt(1)}function h(f){if(f<32)return(f<16?"\\x0":"\\x")+f.toString(16);f=String.fromCharCode(f);return f==="\\"||f==="-"||f==="]"||f==="^"?"\\"+f:
f}function b(f){var b=f.substring(1,f.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),f=[],a=b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,g=b.length;a<g;++a){var k=b[a];if(/\\[bdsw]/i.test(k))c.push(k);else{var k=d(k),o;a+2<g&&"-"===b[a+1]?(o=d(b[a+2]),a+=2):o=k;f.push([k,o]);o<65||k>122||(o<65||k>90||f.push([Math.max(65,k)|32,Math.min(o,90)|32]),o<97||k>122||f.push([Math.max(97,k)&-33,Math.min(o,122)&-33]))}}f.sort(function(f,a){return f[0]-
a[0]||a[1]-f[1]});b=[];g=[];for(a=0;a<f.length;++a)k=f[a],k[0]<=g[1]+1?g[1]=Math.max(g[1],k[1]):b.push(g=k);for(a=0;a<b.length;++a)k=b[a],c.push(h(k[0])),k[1]>k[0]&&(k[1]+1>k[0]&&c.push("-"),c.push(h(k[1])));c.push("]");return c.join("")}function e(f){for(var a=f.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],g=0,k=0;g<c;++g){var o=a[g];o==="("?++k:"\\"===o.charAt(0)&&(o=+o.substring(1))&&(o<=k?d[o]=-1:a[g]=h(o))}for(g=
1;g<d.length;++g)-1===d[g]&&(d[g]=++j);for(k=g=0;g<c;++g)o=a[g],o==="("?(++k,d[k]||(a[g]="(?:")):"\\"===o.charAt(0)&&(o=+o.substring(1))&&o<=k&&(a[g]="\\"+d[o]);for(g=0;g<c;++g)"^"===a[g]&&"^"!==a[g+1]&&(a[g]="");if(f.ignoreCase&&F)for(g=0;g<c;++g)o=a[g],f=o.charAt(0),o.length>=2&&f==="["?a[g]=b(o):f!=="\\"&&(a[g]=o.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var j=0,F=!1,l=!1,I=0,c=a.length;I<c;++I){var p=a[I];if(p.ignoreCase)l=
!0;else if(/[a-z]/i.test(p.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){F=!0;l=!1;break}}for(var i={b:8,t:9,n:10,v:11,f:12,r:13},q=[],I=0,c=a.length;I<c;++I){p=a[I];if(p.global||p.multiline)throw Error(""+p);q.push("(?:"+e(p)+")")}return RegExp(q.join("|"),l?"gi":"g")}function m(a,d){function h(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)h(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)e[l]="\n",F[l<<1]=j++,F[l++<<1|1]=a}}else if(c==
3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),e[l]=c,F[l<<1]=j,j+=c.length,F[l++<<1|1]=a)}var b=/(?:^|\s)nocode(?:\s|$)/,e=[],j=0,F=[],l=0;h(a);return{a:e.join("").replace(/\n$/,""),d:F}}function n(a,d,h,b){d&&(a={a:d,e:a},h(a),b.push.apply(b,a.g))}function x(a){for(var d=void 0,h=a.firstChild;h;h=h.nextSibling)var b=h.nodeType,d=b===1?d?a:h:b===3?S.test(h.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function h(a){for(var l=a.e,j=[l,"pln"],c=
0,p=a.a.match(e)||[],m={},q=0,f=p.length;q<f;++q){var B=p[q],y=m[B],u=void 0,g;if(typeof y==="string")g=!1;else{var k=b[B.charAt(0)];if(k)u=B.match(k[1]),y=k[0];else{for(g=0;g<i;++g)if(k=d[g],u=B.match(k[1])){y=k[0];break}u||(y="pln")}if((g=y.length>=5&&"lang-"===y.substring(0,5))&&!(u&&typeof u[1]==="string"))g=!1,y="src";g||(m[B]=y)}k=c;c+=B.length;if(g){g=u[1];var o=B.indexOf(g),H=o+g.length;u[2]&&(H=B.length-u[2].length,o=H-g.length);y=y.substring(5);n(l+k,B.substring(0,o),h,j);n(l+k+o,g,A(y,
g),j);n(l+k+H,B.substring(H),h,j)}else j.push(l+k,y)}a.g=j}var b={},e;(function(){for(var h=a.concat(d),l=[],i={},c=0,p=h.length;c<p;++c){var m=h[c],q=m[3];if(q)for(var f=q.length;--f>=0;)b[q.charAt(f)]=m;m=m[1];q=""+m;i.hasOwnProperty(q)||(l.push(m),i[q]=r)}l.push(/[\S\s]/);e=j(l)})();var i=d.length;return h}function t(a){var d=[],h=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,
r,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,r,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,r,"\"'"]);a.verbatimStrings&&h.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,r]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,r,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,
r,"#"]),h.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,r])):d.push(["com",/^#[^\n\r]*/,r,"#"]));a.cStyleComments&&(h.push(["com",/^\/\/[^\n\r]*/,r]),h.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,r]));if(b=a.regexLiterals){var e=(b=b>1?"":"\n\r")?".":"[\\S\\s]";h.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+
("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+e+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+e+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&h.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&h.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),r]);d.push(["pln",/^\s+/,r," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");h.push(["lit",/^@[$_a-z][\w$@]*/i,r],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,r],["pln",/^[$_a-z][\w$@]*/i,r],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,
r,"0123456789"],["pln",/^\\[\S\s]?/,r],["pun",RegExp(b),r]);return C(d,h)}function z(a,d,h){function b(a){var c=a.nodeType;if(c==1&&!j.test(a.className))if("br"===a.nodeName)e(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&h){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(l.createTextNode(d),a.nextSibling),e(a),c||a.parentNode.removeChild(a)}}
function e(a){function b(a,c){var d=c?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),h=a.nextSibling;f.appendChild(d);for(var e=h;e;e=h)h=e.nextSibling,f.appendChild(e)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var j=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,l=a.ownerDocument,i=l.createElement("li");a.firstChild;)i.appendChild(a.firstChild);for(var c=[i],p=0;p<c.length;++p)b(c[p]);d===(d|0)&&c[0].setAttribute("value",
d);var n=l.createElement("ol");n.className="linenums";for(var d=Math.max(0,d-1|0)||0,p=0,q=c.length;p<q;++p)i=c[p],i.className="L"+(p+d)%10,i.firstChild||i.appendChild(l.createTextNode("\u00a0")),n.appendChild(i);a.appendChild(n)}function i(a,d){for(var h=d.length;--h>=0;){var b=d[h];U.hasOwnProperty(b)?V.console&&console.warn("cannot override language handler %s",b):U[b]=a}}function A(a,d){if(!a||!U.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return U[a]}function D(a){var d=
a.h;try{var h=m(a.c,a.i),b=h.a;a.a=b;a.d=h.d;a.e=0;A(d,b)(a);var e=/\bMSIE\s(\d+)/.exec(navigator.userAgent),e=e&&+e[1]<=8,d=/\n/g,i=a.a,j=i.length,h=0,l=a.d,n=l.length,b=0,c=a.g,p=c.length,t=0;c[p]=j;var q,f;for(f=q=0;f<p;)c[f]!==c[f+2]?(c[q++]=c[f++],c[q++]=c[f++]):f+=2;p=q;for(f=q=0;f<p;){for(var x=c[f],y=c[f+1],u=f+2;u+2<=p&&c[u+1]===y;)u+=2;c[q++]=x;c[q++]=y;f=u}c.length=q;var g=a.c,k;if(g)k=g.style.display,g.style.display="none";try{for(;b<n;){var o=l[b+2]||j,H=c[t+2]||j,u=Math.min(o,H),E=l[b+
1],W;if(E.nodeType!==1&&(W=i.substring(h,u))){e&&(W=W.replace(d,"\r"));E.nodeValue=W;var Z=E.ownerDocument,s=Z.createElement("span");s.className=c[t+1];var z=E.parentNode;z.replaceChild(s,E);s.appendChild(E);h<o&&(l[b+1]=E=Z.createTextNode(i.substring(u,o)),z.insertBefore(E,s.nextSibling))}h=u;h>=o&&(b+=2);h>=H&&(t+=2)}}finally{if(g)g.style.display=k}}catch(v){V.console&&console.log(v&&v.stack||v)}}var V=window,G=["break,continue,do,else,for,if,return,while"],O=[[G,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],J=[O,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],K=[O,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
L=[K,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],O=[O,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],M=[G,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
N=[G,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],R=[G,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],G=[G,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],Q=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
S=/\S/,T=t({keywords:[J,L,O,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",M,N,G],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),U={};i(T,["default-code"]);i(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);i(C([["pln",/^\s+/,r," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,r,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);i(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);i(t({keywords:J,hashComments:!0,cStyleComments:!0,types:Q}),["c","cc","cpp","cxx","cyc","m"]);i(t({keywords:"null,true,false"}),["json"]);i(t({keywords:L,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:Q}),
["cs"]);i(t({keywords:K,cStyleComments:!0}),["java"]);i(t({keywords:G,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);i(t({keywords:M,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);i(t({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);i(t({keywords:N,
hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);i(t({keywords:O,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);i(t({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);i(t({keywords:R,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);
i(C([],[["str",/^[\S\s]+/]]),["regex"]);var X=V.PR={createSimpleLexer:C,registerLangHandler:i,sourceDecorator:t,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,e){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;e&&z(b,e,!0);D({h:d,j:e,c:b,i:1});return b.innerHTML},
prettyPrint:e=e=function(a,d){function e(){for(var b=V.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;p<j.length&&c.now()<b;p++){for(var d=j[p],m=k,l=d;l=l.previousSibling;){var n=l.nodeType,s=(n===7||n===8)&&l.nodeValue;if(s?!/^\??prettify\b/.test(s):n!==3||/\S/.test(l.nodeValue))break;if(s){m={};s.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){m[b]=c});break}}l=d.className;if((m!==k||f.test(l))&&!w.test(l)){n=!1;for(s=d.parentNode;s;s=s.parentNode)if(g.test(s.tagName)&&s.className&&f.test(s.className)){n=
!0;break}if(!n){d.className+=" prettyprinted";n=m.lang;if(!n){var n=l.match(q),A;if(!n&&(A=x(d))&&u.test(A.tagName))n=A.className.match(q);n&&(n=n[1])}if(y.test(d.tagName))s=1;else var s=d.currentStyle,v=i.defaultView,s=(s=s?s.whiteSpace:v&&v.getComputedStyle?v.getComputedStyle(d,r).getPropertyValue("white-space"):0)&&"pre"===s.substring(0,3);v=m.linenums;if(!(v=v==="true"||+v))v=(v=l.match(/\blinenums\b(?::(\d+))?/))?v[1]&&v[1].length?+v[1]:!0:!1;v&&z(d,v,s);t={h:n,c:d,j:v,i:s};D(t)}}}p<j.length?
P(e,250):"function"===typeof a&&a()}for(var b=d||document.body,i=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],j=[],m=0;m<b.length;++m)for(var l=0,n=b[m].length;l<n;++l)j.push(b[m][l]);var b=r,c=Date;c.now||(c={now:function(){return+new Date}});var p=0,t,q=/\blang(?:uage)?-([\w.]+)(?!\S)/,f=/\bprettyprint\b/,w=/\bprettyprinted\b/,y=/pre|xmp/i,u=/^code$/i,g=/^(?:pre|code|xmp)$/i,k={};e()}};typeof define==="function"&&define.amd&&
define("google-code-prettify",[],function(){return X})})();return e}();R||P(Q,0)})();}()

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Unauthorized | Pastey</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
<link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head>
<body>
<header>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
<div class="container-fluid">
<!-- Navbar logo -->
<a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" />
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/new">New Paste</a>
</li>
{% if whitelisted %}
<li class="nav-item">
<a class="nav-link" href="/config">Config</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light">
<h1 class="error">401 Unauthorized</h1>
</div>
<!-- Jumbotron -->
</header>
<!--Main layout-->
<main class="my-5">
<div class="container">
</div>
</main>
<!--Main layout-->
<!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script>
</body>
</html>

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Not Found | Pastey</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
<link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head>
<body>
<header>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
<div class="container-fluid">
<!-- Navbar logo -->
<a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" />
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/new">New Paste</a>
</li>
{% if whitelisted %}
<li class="nav-item">
<a class="nav-link" href="/config">Config</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light">
<h1 class="error">404 Not Found</h1>
</div>
<!-- Jumbotron -->
</header>
<!--Main layout-->
<main class="my-5">
<div class="container">
</div>
</main>
<!--Main layout-->
<!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script>
</body>
</html>

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Config | Pastey</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
<link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head>
<body>
<header>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
<div class="container-fluid">
<!-- Navbar logo -->
<a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" />
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/new">New Paste</a>
</li>
{% if whitelisted %}
<li class="nav-item">
<a class="nav-link" aria-current="page" href="/config">Config</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light">
<h1 class="mb-3 h2">Config</h1>
</div>
<!-- Jumbotron -->
</header>
<!--Main layout-->
<main class="my-5">
<div class="container">
<div class="row">
<div class="col-md-12">
<table class="table">
<thead>
<tr>
<th scope="col">Option</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
{% for key, value in config_items.items() %}
<tr>
<th scope="row">{{ key}}</th>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div><br />
<div class="row text-center">
<div class="col-md-12">
<h1 class="mb-3 h4">Download Script</h1>
<p>Pastey provides a script that can be used to paste output directly from the command line:</p>
<pre>$ cat /var/log/nginx.log | pastey</pre>
<pre>$ echo "Hello, Pastey!" | pastey</pre>
<p style="font-weight:bold;">Download the following, make it executable, and put it in your system PATH to be used anywhere!</p>
</div>
</div><br />
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-5">
<input class="form-control" id="paste-url" type="text" value="{{ script_url }}" readonly />
</div>
<div class="col-md-1">
<a href="{{ script_url }}">
<button type="button" class="btn btn-success">Download</button>
</a>
</div>
</div>
</div>
</main>
<!--Main layout-->
<!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script>
</body>
</html>

@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Home | Pastey</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
<link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head>
<body>
<header>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
<div class="container-fluid">
<!-- Navbar logo -->
<a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" />
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active">
<a class="nav-link" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/new">New Paste</a>
</li>
{% if whitelisted %}
<li class="nav-item">
<a class="nav-link" href="/config">Config</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light">
<h1 class="mb-3 h2">Welcome to Pastey</h1>
<p class="mb-3">A lightweight, self-hosted paste platform</p>
<a class="btn btn-primary m-2" href="/new" role="button" rel="nofollow">New Paste</a>
</div>
<!-- Jumbotron -->
</header>
<!--Main layout-->
<main class="my-5">
<div class="container">
<div class="row">
<div class="col-md-12 mb-4">
<section>
<!-- Paste -->
{% for paste in pastes %}
<div class="row">
<div class="col-md-4 mb-4">
<div class="bg-image hover-overlay shadow-1-strong rounded ripple" data-mdb-ripple-color="light">
<img src="/static/img/language/{{ paste.icon }}.png" class="img-fluid" />
<a href="/view/{{ paste.unique_id }}">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.15);"></div>
</a>
</div>
</div>
<div class="col-md-8 mb-4">
<a class="btn btn-danger m-2" href="/delete/{{ paste.unique_id }}" role="button" style="float:right;">Delete</a>
<h5>{{ paste.title }}</h5>
<pre style="overflow-x:hidden;">{{ paste.content }}</pre>
</div>
</div>
{% endfor %}
</section>
</div>
</div>
</div>
</main>
<!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script>
</body>
</html>

@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>New | Pastey</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
<link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head>
<body>
<header>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
<div class="container-fluid">
<!-- Navbar logo -->
<a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" />
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" aria-current="page" href="/new">New Paste</a>
</li>
{% if whitelisted %}
<li class="nav-item">
<a class="nav-link" href="/config">Config</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light">
<h1 class="mb-3 h2">New Paste</h1>
</div>
<!-- Jumbotron -->
</header>
<!--Main layout-->
<main class="my-5">
<div class="container">
<form action="/paste" method="POST">
<div class="row">
<div class="col-md-6 mb-4">
<div class="md-form">
<div class="form-outline">
<input type="text" id="title" name="title" class="form-control" />
<label class="form-label" for="title">Paste Title</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-4">
<div class="md-form">
<textarea name="content" id="content" class="md-textarea form-control" rows="20"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-2">
<div class="form-check paste-checkbox">
<input class="form-check-input" type="checkbox" value="" id="encrypt" name="encrypt"
data-mdb-toggle="tooltip" title="Your paste's content will not be accessible without its accompanying decryption key" />
<label class="form-check-label" for="encrypt">
Encrypt
</label>
</div>
</div>
<div class="col-md-2">
<div class="form-check paste-checkbox">
<input class="form-check-input" type="checkbox" value="" id="single" name="single"
data-mdb-toggle="tooltip" title="After generation, the paste will be viewable one time before deletion" />
<label class="form-check-label" for="single">
Single Use
</label>
</div>
</div>
<div class="col-md-4"></div>
<div class="col-md-4">
<button type="submit" class="btn btn-primary btn-block">Paste</button>
</div>
</div>
</form>
</div>
</main>
<!--Main layout-->
<!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script>
</body>
</html>

@ -0,0 +1,15 @@
#!/bin/bash
if ! command -v curl &> /dev/null ; then
echo "Please install curl to use this script"
exit 1
fi
PASTEY_ENDPOINT="{{ endpoint }}"
PASTEY_CONTENT=$(</dev/stdin)
# Submit paste
PASTEY_LINK=$(curl -s -X POST -H "Content-Type: text/plain" --data "${PASTEY_CONTENT}" "${PASTEY_ENDPOINT}")
# Print link
echo "${PASTEY_LINK}"

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>{{ paste.title }} | Pastey</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.11.2/css/all.css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" />
<link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/prettify.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head>
<body>
<header>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
<div class="container-fluid">
<!-- Navbar logo -->
<a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" />
</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/new" rel="nofollow">New Paste</a>
</li>
{% if whitelisted %}
<li class="nav-item">
<a class="nav-link" href="/config">Config</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i>
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light">
{% if paste.encrypted %}
<img src="/static/img/encrypted.png" width="30px" height="30px" /><br /><br />
{% else %}
<img src="/static/img/unencrypted.png" width="30px" height="30px" /><br /><br />
{% endif %}
<h1 class="mb-3 h2">{{ paste.title }}</h1>
<span style="font-weight:bold;">Format: </span><span>{{ paste.language }}</span><br />
<span style="font-weight:bold;">Date: </span><span>{{ paste.timestamp }}</span><br /><br />
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-5">
<input class="form-control" id="paste-url" type="text" value="{{ url }}" readonly />
</div>
<div class="col-md-1">
<button type="button" class="btn btn-success" onclick="copyToClipboard()">Copy Link</button>
</div>
</div>
{% if paste.uses == 0 %}
<div class="row">
<div class="col-md-12">
<br /><span class="badge rounded-pill bg-warning text-dark">Warning: this paste has been set to single use. It will be deleted and unaccessible upon page reload.</span>
</div>
</div>
{% endif %}
</div>
<!-- Jumbotron -->
</header>
<!--Main layout-->
<main class="my-5">
<div class="container">
<form action="/paste" method="POST">
<div class="row">
<div class="col-md-12 mb-4">
<pre class="prettyprint linenums">{{ paste.content }}</pre>
</div>
</div>
</form>
</div>
</main>
<!--Main layout-->
<!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script>
<script type="text/javascript" src="/static/js/common.js"></script>
<script src="/static/js/prettify.min.js"></script>
<script src="/static/js/run_prettify.js"></script></body>
</html>
Loading…
Cancel
Save