Change directory structure, add themes, add expiration, add config options

pull/5/head
Cesura 4 years ago
parent f773d2c45a
commit 2c966143be

296
app.py

@ -2,16 +2,13 @@ from flask import Flask, render_template, request, redirect, abort
from flask_limiter import Limiter from flask_limiter import Limiter
from flask_limiter.util import get_remote_address from flask_limiter.util import get_remote_address
from guesslang import Guess from guesslang import Guess
from datetime import datetime from os import environ
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 from distutils.util import strtobool
from threading import Thread
pastey_version = "0.3"
loaded_config = {}
loaded_themes = []
app = Flask(__name__) app = Flask(__name__)
limiter = Limiter( limiter = Limiter(
@ -20,9 +17,7 @@ limiter = Limiter(
) )
guess = Guess() guess = Guess()
# Pastey version from pastey import config, common, routes, functions
pastey_version = "0.2"
loaded_config = {}
# Check environment variable overrides # Check environment variable overrides
config.data_directory = environ["PASTEY_DATA_DIRECTORY"] if "PASTEY_DATA_DIRECTORY" in environ else config.data_directory config.data_directory = environ["PASTEY_DATA_DIRECTORY"] if "PASTEY_DATA_DIRECTORY" in environ else config.data_directory
@ -37,270 +32,11 @@ config.recent_pastes = int(environ["PASTEY_RECENT_PASTES"]) if "PASTEY_RECENT_PA
config.whitelist_cidr = environ["PASTEY_WHITELIST_CIDR"].split(",") if "PASTEY_WHITELIST_CIDR" in environ else config.whitelist_cidr 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.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 config.behind_proxy = bool(strtobool(environ["PASTEY_BEHIND_PROXY"])) if "PASTEY_BEHIND_PROXY" in environ else config.behind_proxy
config.default_theme = environ["PASTEY_DEFAULT_THEME"] if "PASTEY_DEFAULT_THEME" in environ else config.default_theme
# Check request IP is in config whitelist config.purge_interval = int(environ["PASTEY_PURGE_INTERVAL"]) if "PASTEY_PURGE_INTERVAL" in environ else config.purge_interval
def verify_whitelist(ip): config.force_show_recent = bool(strtobool(environ["PASTEY_FORCE_SHOW_RECENT"])) if "PASTEY_FORCE_SHOW_RECENT" in environ else config.force_show_recent
address = ipaddress.ip_address(ip) config.ignore_guess = environ["PASTEY_IGNORE_GUESS"].split(",") if "PASTEY_IGNORE_GUESS" in environ else config.ignore_guess
config.show_cli_button = bool(strtobool(environ["PASTEY_SHOW_CLI_BUTTON"])) if "PASTEY_SHOW_CLI_BUTTON" in environ else config.show_cli_button
# 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 # Main loop
if __name__ == "__main__": if __name__ == "__main__":
@ -317,7 +53,11 @@ if __name__ == "__main__":
print("=====================================") print("=====================================")
# Register error handlers # Register error handlers
app.register_error_handler(404, page_not_found) app.register_error_handler(404, routes.page_not_found)
app.register_error_handler(401, unauthorized) app.register_error_handler(401, routes.unauthorized)
# Start purging expired pastes thread
purge_thread = Thread(target=functions.purge_expired_pastes, daemon=True)
purge_thread.start()
app.run(host=config.listen_address, port=config.listen_port) app.run(host=config.listen_address, port=config.listen_port)

@ -0,0 +1,87 @@
from . import config
import ipaddress
from os import path
from pathlib import Path
from datetime import datetime, timedelta
########## Common functions ##########
# 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
# Determine theme by checking for cookie, or returning default
def set_theme(request):
if 'pastey_theme' in request.cookies:
return request.cookies['pastey_theme']
return config.default_theme
# Get a sorted list of all themes in the theme dir
def get_themes():
themes = []
for path in Path("./static/themes/").iterdir():
themes.append(str(path).split('/')[-1].split('.')[0])
return sorted(themes, key=str.casefold)
# Get file path from unique id
# This is a wrapper to check for files with the .expires extension
def determine_file(unique_id):
attempt = config.data_directory + "/" + unique_id
if path.exists(attempt):
return attempt
# Check for expiration format
attempt = attempt + ".expires"
if path.exists(attempt):
return attempt
return None
# Take a paste object and check if it is expired
def is_expired(paste):
if 'expiration' in paste and paste['expiration'] != "":
expires = datetime.strptime(paste['expiration'], "%a, %d %b %Y at %H:%M:%S")
if expires < datetime.now():
return True
return False

@ -0,0 +1,52 @@
# 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
# Default theme to display to users
default_theme = "Light"
# Purge interval (in seconds) for checking expired pastes
purge_interval = 3600
# Show recent pastes, even to non-whitelisted users (without a delete button)
force_show_recent = False
# Ignore these classifications for language detection
ignore_guess = ['TeX', 'SQL']
# Show CLI button on home page
show_cli_button = True

@ -0,0 +1,143 @@
from __main__ import guess, app
from . import config, common
from os import path, remove
from pathlib import Path
from datetime import datetime, timedelta
from cryptography.fernet import Fernet
import time
import uuid
import json
########## 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())
basename = path.basename(paths[i])
paste['unique_id'] = basename[:-8] if basename.endswith(".expires") else basename
paste['content'] = '\n'.join(paste['content'].splitlines()[0:10])
paste['icon'] = common.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=""):
file_path = common.determine_file(unique_id)
if file_path is not None:
with open(file_path, "r") as fp:
paste = json.loads(fp.read())
# Check if paste is expired
if common.is_expired(paste):
delete_paste(unique_id)
return None
# 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(file_path, "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 = common.determine_file(unique_id)
if paste is not None:
remove(paste)
# Create new paste
def new_paste(title, content, source_ip, expires=0, single=False, encrypt=False):
unique_id = str(uuid.uuid4())
output_file = config.data_directory + "/" + unique_id
# Check for existing paste id (unlikely)
while path.exists(output_file) or path.exists(output_file + ".expires"):
unique_id = str(uuid.uuid4())
output_file = config.data_directory + "/" + unique_id
# Attempt to guess programming language
guesses = guess.probabilities(content)
language = guesses[0][0] if guesses[0][1] > config.guess_threshold and guesses[0][0] not in config.ignore_guess 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
# Check for expiration
now = datetime.now()
expiration = ""
if expires > 0:
expiration = (now + timedelta(hours=expires)).strftime("%a, %d %b %Y at %H:%M:%S")
output_file = output_file + ".expires"
output = {
"timestamp": now.strftime("%a, %d %b %Y at %H:%M:%S"),
"language": language,
"source_ip": source_ip,
"title": title,
"content": content,
"encrypted": encrypt,
"uses": uses,
"expiration": expiration
}
# Write to output file
with open(output_file, "w+") as fp:
fp.write(json.dumps(output))
return unique_id, key
# Purge expired pastes
def purge_expired_pastes():
print("Starting purge thread, with interval {0} seconds...".format(config.purge_interval))
while True:
for paste in Path(config.data_directory).iterdir():
if str(paste).endswith(".expires"):
unique_id = path.basename(paste)[:-8]
with open(paste, "r") as fp:
content = json.loads(fp.read())
# Check if paste is expired
if common.is_expired(content):
delete_paste(unique_id)
# Sleep for specified interval
time.sleep(config.purge_interval)

@ -0,0 +1,163 @@
from __main__ import app, limiter, loaded_config
from . import config, common, functions
from flask import Flask, render_template, request, redirect, abort
from urllib.parse import quote
from datetime import datetime
# Load themes
loaded_themes = common.get_themes()
########## Routes ##########
# Home page
@app.route("/")
def home():
whitelisted = common.verify_whitelist(common.get_source_ip(request))
pastes = []
if whitelisted or config.force_show_recent:
pastes = functions.get_recent()
return render_template("index.html",
pastes=pastes,
whitelisted=whitelisted,
active_theme=common.set_theme(request),
themes=loaded_themes,
force_show_recent=config.force_show_recent,
show_cli_button=config.show_cli_button,
script_url=request.url.rsplit('/', 1)[0] + "/pastey")
# New paste page
@app.route("/new")
def new():
whitelisted = common.verify_whitelist(common.get_source_ip(request))
return render_template("new.html",
whitelisted=whitelisted,
active_theme=common.set_theme(request),
themes=loaded_themes)
# Config page
@app.route("/config")
def config_page():
whitelisted = common.verify_whitelist(common.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,
active_theme=common.set_theme(request),
themes=loaded_themes)
# View paste page
@app.route("/view/<unique_id>")
def view(unique_id):
content = functions.get_paste(unique_id)
if content is not None:
return render_template("view.html",
paste=content,
url=request.url,
whitelisted=common.verify_whitelist(common.get_source_ip(request)),
active_theme=common.set_theme(request),
themes=loaded_themes)
else:
abort(404)
# View paste page (encrypted)
@app.route("/view/<unique_id>/<key>")
def view_key(unique_id, key):
content = functions.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=common.verify_whitelist(common.get_source_ip(request)),
active_theme=common.set_theme(request),
themes=loaded_themes)
else:
abort(404)
# Delete paste
@app.route("/delete/<unique_id>")
def delete(unique_id):
if not common.verify_whitelist(common.get_source_ip(request)):
abort(401)
functions.delete_paste(unique_id)
return redirect("/")
# Script download
@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: common.verify_whitelist(common.get_source_ip(request)))
def paste():
source_ip = common.get_source_ip(request)
# Check if restrict pasting to whitelist CIDRs is enabled
if config.restrict_pasting and not common.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 = functions.new_paste(title, content, source_ip, expires=int(request.form['expiration']), 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: common.verify_whitelist(common.get_source_ip(request)))
def raw():
source_ip = common.get_source_ip(request)
# Check if restrict pasting to whitelist CIDRs is enabled
if config.restrict_raw_pasting and not common.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):
return render_template('404.html',
whitelisted=common.verify_whitelist(common.get_source_ip(request)),
active_theme=common.set_theme(request),
themes=loaded_themes), 404
# Custom 401 handler
@app.errorhandler(401)
def unauthorized(e):
return render_template('401.html',
whitelisted=common.verify_whitelist(common.get_source_ip(request)),
active_theme=common.set_theme(request),
themes=loaded_themes), 401

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 129 KiB

@ -7,12 +7,34 @@
#intro { #intro {
margin-top: 45px; margin-top: 45px;
} }
.pastey-checkbox {
margin-bottom:20px;
}
}
.tooltip {
pointer-events: none;
} }
.paste-checkbox { .prettyprint {
padding-bottom: 20px; overflow-x: auto;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
} }
.error { .error {
font-size: 60px; font-size: 60px;
} }
.pastey-logo {
width:160px;
height:26px;
background-size: 160px 26px;
}
.pastey-checkbox {
padding-top:10px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@ -4,3 +4,13 @@ function copyToClipboard() {
copyText.setSelectionRange(0, 99999); copyText.setSelectionRange(0, 99999);
document.execCommand("copy"); document.execCommand("copy");
} }
function setTheme(theme) {
document.cookie = "pastey_theme=" + theme + "; Expires=Thu, 01 Jan 2100 00:00:01 GMT; path=/; SameSite=Lax;";
location.reload();
}
function resetTheme() {
document.cookie = "pastey_theme=; Expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/; SameSite=Lax;";
location.reload();
}

@ -0,0 +1,105 @@
body, tr {
background-color: #262626 !important;
color: rgba(255, 255, 255);
}
.pastey-logo {
background-image: url('/static/img/pastey-dark.png');
}
.pastey-navbar {
background-color: #262626 !important;
}
.pastey-link {
color: rgba(255, 255, 255, 0.80);
}
.pastey-link:hover {
color: rgba(255, 255, 255, 0.65);
}
.pastey-header {
background-color: #2f2f2f;
}
.pastey-input {
color: rgba(255, 255, 255) !important;
background-color: #2f2f2f !important;
}
.pastey-input-title {
color: rgba(255, 255, 255) !important;
}
.pastey-preview-image {
background-color: #2f2f2f;
}
.pastey-select {
background-color: #2f2f2f;
color: rgba(255, 255, 255);
}
.nocode {
color: rgb(255,255,255) !important;
}
/* Google prettify elements */
/* Alternating line colors */
li.L1,li.L3,li.L5,li.L7,li.L9 {
background:#2a2a2a;
}
li.L0,li.L2,li.L4,li.L6,li.L8 {
background:#222222;
}
/*
Syntax highlighting
See Google prettify for more details
*/
.pln, .pun {
color: rgba(255, 255, 255);
}
.kwd {
color: rgb(152, 209, 255);
}
.com {
color: rgb(255, 51, 51);
}
.str {
color: rgb(0, 195, 0);
}
.lit {
color: rgb(0, 161, 161);
}
.tag {
color: rgb(0, 167, 218);
}
.atn {
color: rgb(248, 1, 248);
}
.atv {
color: rgb(0, 195, 0);
}
.dec, .var {
color: rgb(255, 2, 129);
}
.typ {
color: rgb(197, 0, 197);
}
.opn, .clo {
color: rgb(218, 218, 0);
}

@ -0,0 +1,19 @@
.pastey-logo {
background-image: url('/static/img/pastey.png');
}
.pastey-link {
color: rgba(0, 0, 0, 0.55);
}
.pastey-link:hover {
color: rgba(0, 0, 0, 0.70);;
}
/*
Syntax highlighting
See Google prettify for more details
*/
.pun {
color: rgba(0, 0, 0);
}

@ -9,38 +9,61 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" /> <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/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" /> <link rel="stylesheet" href="/static/css/style.css" />
<link rel="stylesheet" href="/static/themes/{{ active_theme }}.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/> <link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head> </head>
<body> <body>
<header> <header>
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <nav class="navbar navbar-expand-lg fixed-top bg-white pastey-navbar">
<div class="container-fluid"> <div class="container-fluid">
<!-- Navbar logo --> <!-- Navbar logo -->
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" /> <div class="pastey-logo" style="margin-top: -3px;"></div>
</a> </a>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="/">Home</a> <a class="nav-link pastey-link" href="/">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/new">New Paste</a> <a class="nav-link pastey-link" href="/new">New Paste</a>
</li> </li>
{% if whitelisted %} {% if whitelisted %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/config">Config</a> <a class="nav-link pastey-link" href="/config">Config</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle pastey-link"
href="#"
id="theme"
role="button"
data-mdb-toggle="dropdown"
aria-expanded="false"
>
Theme
</a>
<!-- Theme dropdown -->
<ul class="dropdown-menu pastey-dropdown" aria-labelledby="navbarDropdown">
{% for theme in themes %}
<li><a class="dropdown-item pastey-dropdown-item" href="#" onclick="setTheme('{{ theme }}');">{{ theme }}</a></li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item pastey-dropdown-item" href="#" onclick="resetTheme();">Reset Theme</a>
</li>
</ul>
</li>
</ul> </ul>
<ul class="navbar-nav d-flex flex-row"> <ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0"> <li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank"> <a class="nav-link pastey-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
</li> </li>
@ -51,7 +74,7 @@
<!-- Navbar --> <!-- Navbar -->
<!-- Jumbotron --> <!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light"> <div id="intro" class="p-5 text-center pastey-header">
<h1 class="error">401 Unauthorized</h1> <h1 class="error">401 Unauthorized</h1>
</div> </div>
<!-- Jumbotron --> <!-- Jumbotron -->
@ -68,5 +91,6 @@
<!--JS--> <!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script> <script type="text/javascript" src="/static/js/mdb.min.js"></script>
<script type="text/javascript" src="/static/js/common.js"></script>
</body> </body>
</html> </html>

@ -9,38 +9,61 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" /> <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/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" /> <link rel="stylesheet" href="/static/css/style.css" />
<link rel="stylesheet" href="/static/themes/{{ active_theme }}.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/> <link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head> </head>
<body> <body>
<header> <header>
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <nav class="navbar navbar-expand-lg fixed-top bg-white pastey-navbar">
<div class="container-fluid"> <div class="container-fluid">
<!-- Navbar logo --> <!-- Navbar logo -->
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" /> <div class="pastey-logo" style="margin-top: -3px;"></div>
</a> </a>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="/">Home</a> <a class="nav-link pastey-link" href="/">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/new">New Paste</a> <a class="nav-link pastey-link" href="/new">New Paste</a>
</li> </li>
{% if whitelisted %} {% if whitelisted %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/config">Config</a> <a class="nav-link pastey-link" href="/config">Config</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle pastey-link"
href="#"
id="theme"
role="button"
data-mdb-toggle="dropdown"
aria-expanded="false"
>
Theme
</a>
<!-- Theme dropdown -->
<ul class="dropdown-menu pastey-dropdown" aria-labelledby="navbarDropdown">
{% for theme in themes %}
<li><a class="dropdown-item pastey-dropdown-item" href="#" onclick="setTheme('{{ theme }}');">{{ theme }}</a></li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item pastey-dropdown-item" href="#" onclick="resetTheme();">Reset Theme</a>
</li>
</ul>
</li>
</ul> </ul>
<ul class="navbar-nav d-flex flex-row"> <ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0"> <li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank"> <a class="nav-link pastey-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
</li> </li>
@ -51,7 +74,7 @@
<!-- Navbar --> <!-- Navbar -->
<!-- Jumbotron --> <!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light"> <div id="intro" class="p-5 text-center pastey-header">
<h1 class="error">404 Not Found</h1> <h1 class="error">404 Not Found</h1>
</div> </div>
<!-- Jumbotron --> <!-- Jumbotron -->
@ -68,5 +91,6 @@
<!--JS--> <!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script> <script type="text/javascript" src="/static/js/mdb.min.js"></script>
<script type="text/javascript" src="/static/js/common.js"></script>
</body> </body>
</html> </html>

@ -9,38 +9,61 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" /> <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/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" /> <link rel="stylesheet" href="/static/css/style.css" />
<link rel="stylesheet" href="/static/themes/{{ active_theme }}.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/> <link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head> </head>
<body> <body>
<header> <header>
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <nav class="navbar navbar-expand-lg fixed-top bg-white pastey-navbar">
<div class="container-fluid"> <div class="container-fluid">
<!-- Navbar logo --> <!-- Navbar logo -->
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" /> <div class="pastey-logo" style="margin-top: -3px;"></div>
</a> </a>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="/">Home</a> <a class="nav-link pastey-link" href="/">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/new">New Paste</a> <a class="nav-link pastey-link" href="/new">New Paste</a>
</li> </li>
{% if whitelisted %} {% if whitelisted %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" aria-current="page" href="/config">Config</a> <a class="nav-link pastey-link" aria-current="page" href="/config">Config</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle pastey-link"
href="#"
id="theme"
role="button"
data-mdb-toggle="dropdown"
aria-expanded="false"
>
Theme
</a>
<!-- Theme dropdown -->
<ul class="dropdown-menu pastey-dropdown" aria-labelledby="navbarDropdown">
{% for theme in themes %}
<li><a class="dropdown-item pastey-dropdown-item" href="#" onclick="setTheme('{{ theme }}');">{{ theme }}</a></li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item" href="#" onclick="resetTheme();">Reset Theme</a>
</li>
</ul>
</li>
</ul> </ul>
<ul class="navbar-nav d-flex flex-row"> <ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0"> <li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank"> <a class="nav-link pastey-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
</li> </li>
@ -51,7 +74,7 @@
<!-- Navbar --> <!-- Navbar -->
<!-- Jumbotron --> <!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light"> <div id="intro" class="p-5 text-center pastey-header">
<h1 class="mb-3 h2">Config</h1> <h1 class="mb-3 h2">Config</h1>
</div> </div>
<!-- Jumbotron --> <!-- Jumbotron -->
@ -94,7 +117,7 @@
<div class="row"> <div class="row">
<div class="col-md-3"></div> <div class="col-md-3"></div>
<div class="col-md-5"> <div class="col-md-5">
<input class="form-control" id="paste-url" type="text" value="{{ script_url }}" readonly /> <input class="form-control pastey-input" id="paste-url" type="text" value="{{ script_url }}" readonly />
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<a href="{{ script_url }}"> <a href="{{ script_url }}">
@ -109,5 +132,6 @@
<!--JS--> <!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script> <script type="text/javascript" src="/static/js/mdb.min.js"></script>
<script type="text/javascript" src="/static/js/common.js"></script>
</body> </body>
</html> </html>

@ -10,37 +10,61 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" /> <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/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" /> <link rel="stylesheet" href="/static/css/style.css" />
<link rel="stylesheet" href="/static/themes/{{ active_theme }}.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/> <link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head> </head>
<body> <body>
<header> <header>
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <nav class="navbar navbar-expand-lg fixed-top bg-white pastey-navbar">
<div class="container-fluid"> <div class="container-fluid">
<!-- Navbar logo --> <!-- Navbar logo -->
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" /> <div class="pastey-logo" style="margin-top: -3px;"></div>
</a> </a>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" aria-current="page" href="/">Home</a> <a class="nav-link pastey-link" aria-current="page" href="/">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/new">New Paste</a> <a class="nav-link pastey-link" href="/new">New Paste</a>
</li> </li>
{% if whitelisted %} {% if whitelisted %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/config">Config</a> <a class="nav-link pastey-link" href="/config">Config</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle pastey-link"
href="#"
id="theme"
role="button"
data-mdb-toggle="dropdown"
aria-expanded="false"
>
Theme
</a>
<!-- Theme dropdown -->
<ul class="dropdown-menu pastey-dropdown" aria-labelledby="navbarDropdown">
{% for theme in themes %}
<li><a class="dropdown-item pastey-dropdown-item" href="#" onclick="setTheme('{{ theme }}');">{{ theme }}</a></li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item pastey-dropdown-item" href="#" onclick="resetTheme();">Reset Theme</a>
</li>
</ul>
</li>
</ul> </ul>
<ul class="navbar-nav d-flex flex-row"> <ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0"> <li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank"> <a class="nav-link pastey-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
</li> </li>
@ -51,11 +75,26 @@
<!-- Navbar --> <!-- Navbar -->
<!-- Jumbotron --> <!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light"> <div id="intro" class="p-5 text-center pastey-header">
<h1 class="mb-3 h2">Welcome to Pastey</h1> <h1 class="mb-3 h2">Welcome to Pastey</h1>
<p class="mb-3">A lightweight, self-hosted paste platform</p> <p class="mb-3">A lightweight, self-hosted paste platform</p><br />
<div class="row justify-content-md-center">
<div class="col-md-1">
<a class="btn btn-primary m-2" href="/new" role="button" rel="nofollow">New Paste</a> <a class="btn btn-primary m-2" href="/new" role="button" rel="nofollow">New Paste</a>
</div> </div>
{% if show_cli_button %}
<div class="col-md-1">
<div style="margin-top:8px; font-weight:bold; font-size:18px;">or</div>
</div>
<div class="col-md-1">
<a class="btn btn-success m-2" href="{{ script_url }}" role="button"
data-mdb-toggle="tooltip"
title="Pastey provides a script that can be used to pipe output directly from the command line">Use CLI</a>
</div>
{% endif %}
</div>
</div>
<!-- Jumbotron --> <!-- Jumbotron -->
</header> </header>
@ -69,7 +108,7 @@
{% for paste in pastes %} {% for paste in pastes %}
<div class="row"> <div class="row">
<div class="col-md-4 mb-4"> <div class="col-md-4 mb-4">
<div class="bg-image hover-overlay shadow-1-strong rounded ripple" data-mdb-ripple-color="light"> <div class="bg-image hover-overlay shadow-1-strong rounded ripple pastey-preview-image" data-mdb-ripple-color="light">
<img src="/static/img/language/{{ paste.icon }}.png" class="img-fluid" /> <img src="/static/img/language/{{ paste.icon }}.png" class="img-fluid" />
<a href="/view/{{ paste.unique_id }}"> <a href="/view/{{ paste.unique_id }}">
<div class="mask" style="background-color: rgba(251, 251, 251, 0.15);"></div> <div class="mask" style="background-color: rgba(251, 251, 251, 0.15);"></div>
@ -78,7 +117,9 @@
</div> </div>
<div class="col-md-8 mb-4"> <div class="col-md-8 mb-4">
{% if whitelisted %}
<a class="btn btn-danger m-2" href="/delete/{{ paste.unique_id }}" role="button" style="float:right;">Delete</a> <a class="btn btn-danger m-2" href="/delete/{{ paste.unique_id }}" role="button" style="float:right;">Delete</a>
{% endif %}
<h5>{{ paste.title }}</h5> <h5>{{ paste.title }}</h5>
<pre style="overflow-x:hidden;">{{ paste.content }}</pre> <pre style="overflow-x:hidden;">{{ paste.content }}</pre>
@ -93,5 +134,6 @@
<!--JS--> <!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script> <script type="text/javascript" src="/static/js/mdb.min.js"></script>
<script type="text/javascript" src="/static/js/common.js"></script>
</body> </body>
</html> </html>

@ -9,38 +9,61 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" /> <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/mdb.min.css" />
<link rel="stylesheet" href="/static/css/style.css" /> <link rel="stylesheet" href="/static/css/style.css" />
<link rel="stylesheet" href="/static/themes/{{ active_theme }}.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/> <link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head> </head>
<body> <body>
<header> <header>
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <nav class="navbar navbar-expand-lg fixed-top bg-white pastey-navbar">
<div class="container-fluid"> <div class="container-fluid">
<!-- Navbar logo --> <!-- Navbar logo -->
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" /> <div class="pastey-logo" style="margin-top: -3px;"></div>
</a> </a>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="/">Home</a> <a class="nav-link pastey-link" href="/">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" aria-current="page" href="/new">New Paste</a> <a class="nav-link pastey-link" aria-current="page" href="/new">New Paste</a>
</li> </li>
{% if whitelisted %} {% if whitelisted %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/config">Config</a> <a class="nav-link pastey-link" href="/config">Config</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle pastey-link"
href="#"
id="theme"
role="button"
data-mdb-toggle="dropdown"
aria-expanded="false"
>
Theme
</a>
<!-- Theme dropdown -->
<ul class="dropdown-menu pastey-dropdown" aria-labelledby="navbarDropdown">
{% for theme in themes %}
<li><a class="dropdown-item pastey-dropdown-item" href="#" onclick="setTheme('{{ theme }}');">{{ theme }}</a></li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item pastey-dropdown-item" href="#" onclick="resetTheme();">Reset Theme</a>
</li>
</ul>
</li>
</ul> </ul>
<ul class="navbar-nav d-flex flex-row"> <ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0"> <li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank"> <a class="nav-link pastey-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
</li> </li>
@ -51,7 +74,7 @@
<!-- Navbar --> <!-- Navbar -->
<!-- Jumbotron --> <!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light"> <div id="intro" class="p-5 text-center pastey-header">
<h1 class="mb-3 h2">New Paste</h1> <h1 class="mb-3 h2">New Paste</h1>
</div> </div>
<!-- Jumbotron --> <!-- Jumbotron -->
@ -65,8 +88,8 @@
<div class="col-md-6 mb-4"> <div class="col-md-6 mb-4">
<div class="md-form"> <div class="md-form">
<div class="form-outline"> <div class="form-outline">
<input type="text" id="title" name="title" class="form-control" /> <input type="text" id="title" name="title" class="form-control pastey-input" />
<label class="form-label" for="title">Paste Title</label> <label class="form-label pastey-input-title" for="title">Paste Title</label>
</div> </div>
</div> </div>
</div> </div>
@ -75,14 +98,14 @@
<div class="row"> <div class="row">
<div class="col-md-12 mb-4"> <div class="col-md-12 mb-4">
<div class="md-form"> <div class="md-form">
<textarea name="content" id="content" class="md-textarea form-control" rows="20"></textarea> <textarea name="content" id="content" class="md-textarea form-control pastey-input" rows="20"></textarea>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-2">
<div class="form-check paste-checkbox"> <div class="form-check pastey-checkbox">
<input class="form-check-input" type="checkbox" value="" id="encrypt" name="encrypt" <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" /> 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"> <label class="form-check-label" for="encrypt">
@ -92,7 +115,7 @@
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<div class="form-check paste-checkbox"> <div class="form-check pastey-checkbox">
<input class="form-check-input" type="checkbox" value="" id="single" name="single" <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" /> data-mdb-toggle="tooltip" title="After generation, the paste will be viewable one time before deletion" />
<label class="form-check-label" for="single"> <label class="form-check-label" for="single">
@ -100,8 +123,19 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-md-4"></div> <div class="col-md-3">
<select class="form-select form-select-md mb-3 pastey-select" id="expiration" name="expiration">
<option value="-1" selected>Expires never</option>
<option value="1">Expires in 1 hour</option>
<option value="6">Expires in 6 hours</option>
<option value="24">Expires in 1 day</option>
<option value="72">Expires in 3 days</option>
<option value="168">Expires in 1 week</option>
<option value="672">Expires in 4 weeks</option>
<option value="8760">Expires in 1 year</option>
</select>
</div>
<div class="col-md-1"></div>
<div class="col-md-4"> <div class="col-md-4">
<button type="submit" class="btn btn-primary btn-block">Paste</button> <button type="submit" class="btn btn-primary btn-block">Paste</button>
</div> </div>
@ -113,5 +147,6 @@
<!--JS--> <!--JS-->
<script type="text/javascript" src="/static/js/mdb.min.js"></script> <script type="text/javascript" src="/static/js/mdb.min.js"></script>
<script type="text/javascript" src="/static/js/common.js"></script>
</body> </body>
</html> </html>

@ -1,5 +1,9 @@
#!/bin/bash #!/bin/bash
# Install this script into your PATH and chmod +x
# Usage example:
# $ cat /var/log/nginx.log | pastey
if ! command -v curl &> /dev/null ; then if ! command -v curl &> /dev/null ; then
echo "Please install curl to use this script" echo "Please install curl to use this script"
exit 1 exit 1

@ -10,6 +10,7 @@
<link rel="stylesheet" href="/static/css/mdb.min.css" /> <link rel="stylesheet" href="/static/css/mdb.min.css" />
<link rel="stylesheet" href="/static/css/prettify.min.css" /> <link rel="stylesheet" href="/static/css/prettify.min.css" />
<link rel="stylesheet" href="/static/css/style.css" /> <link rel="stylesheet" href="/static/css/style.css" />
<link rel="stylesheet" href="/static/themes/{{ active_theme }}.css" />
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/> <link rel="icon" type="image/x-icon" href="/static/img/favicon.ico"/>
</head> </head>
@ -17,32 +18,54 @@
<header> <header>
<!-- Navbar --> <!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <nav class="navbar navbar-expand-lg fixed-top bg-white pastey-navbar">
<div class="container-fluid"> <div class="container-fluid">
<!-- Navbar logo --> <!-- Navbar logo -->
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="/static/img/pastey.png" height="26" alt="" loading="lazy" style="margin-top: -3px;" /> <div class="pastey-logo" style="margin-top: -3px;"></div>
</a> </a>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="/">Home</a> <a class="nav-link pastey-link" href="/">Home</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/new" rel="nofollow">New Paste</a> <a class="nav-link pastey-link" href="/new" rel="nofollow">New Paste</a>
</li> </li>
{% if whitelisted %} {% if whitelisted %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/config">Config</a> <a class="nav-link pastey-link" href="/config">Config</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item dropdown">
<a
class="nav-link dropdown-toggle pastey-link"
href="#"
id="theme"
role="button"
data-mdb-toggle="dropdown"
aria-expanded="false"
>
Theme
</a>
<!-- Theme dropdown -->
<ul class="dropdown-menu pastey-dropdown" aria-labelledby="navbarDropdown">
{% for theme in themes %}
<li><a class="dropdown-item pastey-dropdown-item" href="#" onclick="setTheme('{{ theme }}');">{{ theme }}</a></li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item pastey-dropdown-item" href="#" onclick="resetTheme();">Reset Theme</a>
</li>
</ul>
</li>
</ul> </ul>
<ul class="navbar-nav d-flex flex-row"> <ul class="navbar-nav d-flex flex-row">
<li class="nav-item me-3 me-lg-0"> <li class="nav-item me-3 me-lg-0">
<a class="nav-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank"> <a class="nav-link pastey-link" href="https://github.com/Cesura/pastey" rel="nofollow" target="_blank">
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
</a> </a>
</li> </li>
@ -53,7 +76,7 @@
<!-- Navbar --> <!-- Navbar -->
<!-- Jumbotron --> <!-- Jumbotron -->
<div id="intro" class="p-5 text-center bg-light"> <div id="intro" class="p-5 text-center pastey-header">
{% if paste.encrypted %} {% if paste.encrypted %}
<img src="/static/img/encrypted.png" width="30px" height="30px" /><br /><br /> <img src="/static/img/encrypted.png" width="30px" height="30px" /><br /><br />
{% else %} {% else %}
@ -61,12 +84,14 @@
{% endif %} {% endif %}
<h1 class="mb-3 h2">{{ paste.title }}</h1> <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;">Format: </span><span>{{ paste.language }}</span><br />
<span style="font-weight:bold;">Date: </span><span>{{ paste.timestamp }}</span><br /><br /> <span style="font-weight:bold;">Date: </span><span>{{ paste.timestamp }}</span><br />
{% if paste.expiration is defined and paste.expiration != "" %}
<span style="font-weight:bold;">Expires: </span><span>{{ paste.expiration }}</span><br />
{% endif %}<br />
<div class="row"> <div class="row">
<div class="col-md-3"></div> <div class="col-md-3"></div>
<div class="col-md-5"> <div class="col-md-5">
<input class="form-control" id="paste-url" type="text" value="{{ url }}" readonly /> <input class="form-control pastey-input" id="paste-url" type="text" value="{{ url }}" readonly />
</div> </div>
<div class="col-md-1"> <div class="col-md-1">
<button type="button" class="btn btn-success" onclick="copyToClipboard()">Copy Link</button> <button type="button" class="btn btn-success" onclick="copyToClipboard()">Copy Link</button>
@ -89,7 +114,7 @@
<form action="/paste" method="POST"> <form action="/paste" method="POST">
<div class="row"> <div class="row">
<div class="col-md-12 mb-4"> <div class="col-md-12 mb-4">
<pre class="prettyprint linenums">{{ paste.content }}</pre> <pre class="prettyprint linenums">{% if paste.language == 'Plaintext' %}<span class="nocode">{% endif %}{{ paste.content }}{% if paste.language == 'Plaintext' %}</span>{% endif %}</pre>
</div> </div>
</div> </div>
</form> </form>

Loading…
Cancel
Save