Applied PEP 8 style. | Added number lines on view_paste. | Improved some of the code and fixed some bugs.

This commit is contained in:
ComputerTech312 2024-03-27 19:20:58 +01:00
parent 577fa4683c
commit f22a79429a
7 changed files with 88 additions and 107 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ nohup.out
cert.pem
key.pem
pastes.db
venv

View file

@ -1,27 +1,29 @@
from flask import Flask, request, render_template, url_for
from datetime import datetime, timedelta
import sys
import secrets
import string
import sqlite3
import string
import sys
from datetime import datetime, timedelta
from flask import Flask, request, render_template
app = Flask(__name__)
def generate_random_id(length=16):
characters = string.ascii_letters + string.digits
return ''.join(secrets.choice(characters) for _ in range(length))
def parse_expiry_time(expiry_option):
expiry_time = None
if expiry_option == "1_minute":
if expiry_option == '1_minute':
expiry_time = datetime.now() + timedelta(minutes=1)
elif expiry_option == "5_minute":
elif expiry_option == '5_minute':
expiry_time = datetime.now() + timedelta(minutes=5)
elif expiry_option == "10_minute":
elif expiry_option == '10_minute':
expiry_time = datetime.now() + timedelta(minutes=10)
elif expiry_option == "1_hour":
elif expiry_option == '1_hour':
expiry_time = datetime.now() + timedelta(hours=1)
elif expiry_option == "1_day":
elif expiry_option == '1_day':
expiry_time = datetime.now() + timedelta(days=1)
elif expiry_option is None:
expiry_time = None
@ -29,6 +31,7 @@ def parse_expiry_time(expiry_option):
expiry_time = datetime.now() + timedelta(days=1)
return expiry_time
def init_db():
with sqlite3.connect('pastes.db') as conn:
c = conn.cursor()
@ -36,37 +39,40 @@ def init_db():
(id TEXT PRIMARY KEY, content TEXT NOT NULL, expiry TIMESTAMP NOT NULL)''')
conn.commit()
init_db()
@app.route("/api/v1/secure-paste", methods=["POST"])
@app.route('/api/v1/secure-paste', methods=['POST'])
def secure_paste():
if request.method == "POST":
if not "data" in request.json:
print("no data key")
return {"success": False}, 400
if sys.getsizeof(request.json["data"].encode('utf-8')) > 10485760: # Limit paste size to 10MB in bytes
return {"success": False, "error": "Paste too large"}, 400
if request.method == 'POST':
if not 'data' in request.json:
print('no data key')
return {'success': False}, 400
if sys.getsizeof(request.json['data'].encode('utf-8')) > 10485760: # Limit paste size to 10MB in bytes
return {'success': False, 'error': 'Paste too large'}, 400
id = generate_random_id()
expiry_time = parse_expiry_time(request.json.get("expiry", "1_day"))
expiry_time = parse_expiry_time(request.json.get('expiry', '1_day'))
with sqlite3.connect('pastes.db') as conn:
c = conn.cursor()
c.execute("INSERT INTO pastes (id, content, expiry) VALUES (?, ?, ?)",
(id, request.json["data"], expiry_time.strftime('%Y-%m-%d %H:%M:%S')))
c.execute('INSERT INTO pastes (id, content, expiry) VALUES (?, ?, ?)',
(id, request.json['data'], expiry_time.strftime('%Y-%m-%d %H:%M:%S')))
conn.commit()
return {
"success": True,
"id": id
'success': True,
'id': id
}
@app.route("/api/v1/secure-paste/<paste_id>", methods=["GET"])
@app.route('/api/v1/secure-paste/<paste_id>', methods=['GET'])
def secure_paste2(paste_id):
if request.method == "GET":
if request.method == 'GET':
print(request.args)
with sqlite3.connect('pastes.db') as conn:
c = conn.cursor()
c.execute("SELECT content, expiry FROM pastes WHERE id=?", (paste_id,))
c.execute('SELECT content, expiry FROM pastes WHERE id=?', (paste_id,))
paste_data = c.fetchone()
if paste_data:
@ -74,24 +80,24 @@ def secure_paste2(paste_id):
expiry_datetime = datetime.strptime(expiry, '%Y-%m-%d %H:%M:%S')
if expiry_datetime and datetime.now() > expiry_datetime:
c.execute("DELETE FROM pastes WHERE id=?", (paste_id,))
c.execute('DELETE FROM pastes WHERE id=?', (paste_id,))
conn.commit()
return {"success": False, "error": "Paste expired"}, 404
return {'success': False, 'error': 'Paste expired'}, 404
return {"success": True, "data": content}
return {'success': True, 'data': content}
else:
return {"success": False, "error": "Paste not found"}, 404
return {'success': False, 'error': 'Paste not found'}, 404
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/<paste_id>', methods=['GET'])
def view_paste(paste_id):
return render_template("view_paste.html")
return render_template('view_paste.html')
if __name__ == '__main__':
# if os.environ.get("DEBUG_NOSSL"):
app.run(host='0.0.0.0', port=8888, debug=True)
#else:
# app.run(host='0.0.0.0', port=5000, debug=True, ssl_context=('cert.pem', 'key.pem'))
app.run(host='0.0.0.0', port=8888, debug=True)

View file

@ -1,25 +1,3 @@
/*
* client.js: facilitates connection between frontend and RESTful backend
* services via AJAX
*
* Copyright (C) 2023 David Schultz <me@zpld.me>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
function escapeHtml(unsafe)
{
return unsafe
@ -76,10 +54,16 @@ function ajax_pull_paste(id, secret) {
/* content decryption and display */
var decrypted_content = decrypt_paste(secret, response.data);
// Split the decrypted content into lines and wrap each line in a <div> tag
var lines = decrypted_content.split('\n');
var formatted = lines.map(function(line) {
return '<div>' + escapeHtml(line) + '</div>';
}).join('');
document.getElementById("load-status").hidden = true;
var content = document.getElementById("content");
content.hidden = false;
content.innerHTML = `<pre>${escapeHtml(decrypted_content)}</pre>`
content.innerHTML = formatted; // Set the innerHTML to the formatted content
} else if (xhr.status === 404) {
document.getElementById("load-status").innerHTML = "Paste not found or has expired. Please try again later.";
}

View file

@ -1,24 +1,3 @@
/*
* crypto.js: contains cryptographic functions for content encryption
*
* Copyright (C) 2023 David Schultz <me@zpld.me>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
function encrypt_paste(k,c) {
return btoa(CryptoJS.AES.encrypt(c, k));
}

View file

@ -1,4 +1,11 @@
/*
* this script might be used for something later but right now it simply exists as a placeholder
* ~ launchd
*/
document.addEventListener("DOMContentLoaded", (event) => {
var key = window.location.hash.split("#")[1];
console.log(key);
if (!key) {
document.getElementById("load-status").innerHTML = "No secret was supplied. No content will be displayed at this time.";
return;
}
var parts = window.location.href.split("/");
var paste_id = parts[parts.length-1].split("#")[0];
ajax_pull_paste(paste_id, key);
});

View file

@ -84,3 +84,26 @@ input[type="submit"] {
input[type="submit"]:hover {
background-color: #555;
}
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
.line-number {
counter-reset: line;
}
.line-number > div::before {
counter-increment: line;
content: counter(line);
display: inline-block;
width: 30px;
border-right: 1px solid #ddd;
padding: 0 5px;
margin-right: 5px;
text-align: right;
}

View file

@ -6,25 +6,6 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script src="{{ url_for('static', filename='crypto.js') }}"></script>
<script src="{{ url_for('static', filename='client.js') }}"></script>
<script>
document.addEventListener("DOMContentLoaded", (event) => {
var key = window.location.hash.split("#")[1];
console.log(key);
if (!key) {
document.getElementById("load-status").innerHTML = "No secret was supplied. No content will be displayed at this time.";
return;
}
var parts = window.location.href.split("/");
var paste_id = parts[parts.length-1].split("#")[0];
ajax_pull_paste(paste_id, key);
});
</script>
<style>
pre {
white-space: pre-wrap;
overflow-wrap: break-word;
}
</style>
</head>
<body>
<noscript>
@ -35,13 +16,13 @@
<div id="load-status">
Please wait while the content is being loaded...
</div>
<div id="content" class="content" hidden>
<!-- Placeholder. PRE to be added by the AJAX client -->
</div>
<pre id="content" class="content line-number" hidden>
<!-- Placeholder. DIVs to be added by the AJAX client -->
</pre>
</div>
</main>
<footer>
<!-- Footer content goes here -->
</footer>
</body>
</html>
</html>