diff --git a/app.py b/app.py
index a449e5e..b2e39a0 100644
--- a/app.py
+++ b/app.py
@@ -2,6 +2,7 @@ import json
import os
import re
import sqlite3
+import sys
import uuid
import datetime
from flask import Flask, render_template, request, jsonify, abort, Response
@@ -32,6 +33,10 @@ _ui = CFG['ui']
app = Flask(__name__)
app.config['SECRET_KEY'] = _server.get('secret_key', 'change-me')
+if _server.get('debug', False):
+ print('WARNING: debug=true is set in config.json — never use debug mode in production!',
+ file=sys.stderr, flush=True)
+
DATABASE = _db_cfg.get('path', 'bastebin.db')
MAX_ENCRYPTED_BYTES = _pastes.get('max_size_bytes', 2 * 1024 * 1024)
MAX_TITLE_BYTES = 200
@@ -73,23 +78,25 @@ def get_db_connection():
return conn
def init_db():
- conn = sqlite3.connect(DATABASE)
- cursor = conn.cursor()
- cursor.execute("PRAGMA table_info(pastes)")
- columns = {row[1] for row in cursor.fetchall()}
- if columns and 'content' in columns and 'encrypted_data' not in columns:
- cursor.execute("DROP TABLE pastes")
- cursor.execute('''
- CREATE TABLE IF NOT EXISTS pastes (
- id TEXT PRIMARY KEY,
- encrypted_data TEXT NOT NULL,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- expires_at TIMESTAMP,
- views INTEGER DEFAULT 0
- )
- ''')
- conn.commit()
- conn.close()
+ conn = sqlite3.connect(DATABASE)
+ try:
+ cursor = conn.cursor()
+ cursor.execute("PRAGMA table_info(pastes)")
+ columns = {row[1] for row in cursor.fetchall()}
+ if columns and 'content' in columns and 'encrypted_data' not in columns:
+ cursor.execute("DROP TABLE pastes")
+ cursor.execute('''
+ CREATE TABLE IF NOT EXISTS pastes (
+ id TEXT PRIMARY KEY,
+ encrypted_data TEXT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ expires_at TIMESTAMP,
+ views INTEGER DEFAULT 0
+ )
+ ''')
+ conn.commit()
+ finally:
+ conn.close()
# ── Helpers ───────────────────────────────────────────────────────────────────
@@ -112,7 +119,7 @@ def _get_paste_or_abort(paste_id):
abort(404)
if paste['expires_at']:
expires_at = datetime.datetime.fromisoformat(paste['expires_at'])
- if expires_at < datetime.datetime.now():
+ if expires_at < datetime.datetime.utcnow():
abort(410)
return paste
@@ -165,16 +172,24 @@ def create_paste():
'1week': datetime.timedelta(weeks=1),
'1month': datetime.timedelta(days=30),
}
- expires_at = datetime.datetime.now() + delta_map[expires_in]
+ expires_at = datetime.datetime.utcnow() + delta_map[expires_in]
- paste_id = generate_paste_id()
+ paste_id = None
conn = get_db_connection()
try:
- conn.execute(
- 'INSERT INTO pastes (id, encrypted_data, expires_at) VALUES (?, ?, ?)',
- (paste_id, store_data, expires_at)
- )
- conn.commit()
+ for _ in range(5):
+ paste_id = generate_paste_id()
+ try:
+ conn.execute(
+ 'INSERT INTO pastes (id, encrypted_data, expires_at) VALUES (?, ?, ?)',
+ (paste_id, store_data, expires_at)
+ )
+ conn.commit()
+ break
+ except sqlite3.IntegrityError:
+ continue
+ else:
+ return jsonify({'error': 'Service temporarily unavailable'}), 503
finally:
conn.close()
return jsonify({'paste_id': paste_id, 'url': f'/{paste_id}'})
@@ -183,12 +198,17 @@ def create_paste():
def view_paste(paste_id):
if not _PASTE_ID_RE.match(paste_id):
abort(404)
- paste = _get_paste_or_abort(paste_id)
- conn = get_db_connection()
+ conn = get_db_connection()
try:
+ paste = conn.execute('SELECT * FROM pastes WHERE id = ?', (paste_id,)).fetchone()
+ if not paste:
+ abort(404)
+ if paste['expires_at']:
+ expires = datetime.datetime.fromisoformat(paste['expires_at'])
+ if expires < datetime.datetime.utcnow():
+ abort(410)
conn.execute('UPDATE pastes SET views = views + 1 WHERE id = ?', (paste_id,))
conn.commit()
- paste = conn.execute('SELECT * FROM pastes WHERE id = ?', (paste_id,)).fetchone()
finally:
conn.close()
return render_template('view.html', paste=paste)
diff --git a/templates/base.html b/templates/base.html
index 3cb9910..dac9015 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -11,8 +11,8 @@
document.documentElement.setAttribute('data-theme','dark');
})();
-
-
+
+
@@ -35,35 +35,35 @@
{% block content %}{% endblock %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% block scripts %}{% endblock %}