From c1201dd7a6b6cc03a92932e318c5010fe4ceacb6 Mon Sep 17 00:00:00 2001 From: spitkov Date: Mon, 9 Sep 2024 21:23:37 +0200 Subject: [PATCH] added download all to folder view which zips then downloads refactored the whole sotring mechanisms now it stores it in a db NOT IN A FUCKING HTML FILE THATS LIKE SO OPTIMAL also you can add /raw to any text and get it raw uwu :3 major refactor :333 --- .DS_Store | Bin 0 -> 6148 bytes app.py | 199 ++++++++++++++++++++++++++++++------------ schema.sql | 6 ++ templates/folder.html | 72 ++++++++++++--- uploads/.DS_Store | Bin 0 -> 6148 bytes uploads/Finder.png | Bin 18606 -> 0 bytes 6 files changed, 207 insertions(+), 70 deletions(-) create mode 100644 .DS_Store create mode 100644 schema.sql create mode 100644 uploads/.DS_Store delete mode 100644 uploads/Finder.png diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4f1c0bb889e34ef426fe85b989c56199397033db GIT binary patch literal 6148 zcmeHKO>fgc5S>j;W4A)`0i+%+^;(6{AQ0l>rs;u0FAahNpkUWgYvFi9>?oiplF#Ko z@E5rDm-N4If;YQc2|K_EAr$RIvu{79Q(%WCl++pGbA#d2jK=hV=6DjWP2f3|RDfO5$L7wqBG2hkF(Ysa z%$3#GY+W=s9%km1rj#K!t`~I%G9dc$lLl=(A4f~XCu`BH!XKjJMKLw|<8?gE(xU8k zZ$xX$xwGAN+wQLWc6ccl!!j!8ML(Lo=GF@-v-q|i#V^zGV&vaDmU$Va`FN%XX);F0 zn^$R`$VFey^Q2JRp&oEO*BkkJ%jIFWdl2-V^i~JK^64S!k9v<+E6=^Z|KQo#=zTWH za&_TB{ z)YV^qV89)~&|swzJusoEKus0qiXqI+L1;S0Lla+V)N~SRWz1t%7UqT`%*8_lDx5^9 z(Uw*Lt3X|WE#36_{6G2q{l8AKEvtZ4V51Ze&R{s`BPDaTt|iB3Z2*4') def content(vanity): - target = data_store.get(vanity) + db = get_db() + cursor = db.cursor() + cursor.execute("SELECT * FROM content WHERE vanity = ?", (vanity,)) + target = cursor.fetchone() + if target: - if target['type'] == 'pastebin': - return render_template('content.html', content=target['content'], created_at=target['created_at']) - elif target['type'] == 'file': - file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{target["filename"]}') + content_type, content_data = target[1], target[2] + if content_type == 'pastebin': + return render_template('content.html', content=content_data, created_at=target[3]) + elif content_type == 'file': + file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{content_data}') file_info = { - 'name': target['filename'], + 'name': content_data, 'size': os.path.getsize(file_path), 'modified_at': datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M:%S'), 'url': url_for('download_file', vanity=vanity) } return render_template('file.html', **file_info) - elif target['type'] == 'url': - return render_template('content.html', url=target['url']) + elif content_type == 'url': + return render_template('content.html', url=content_data) return 'Not Found', 404 @app.route('/download/', methods=['GET']) def download_file(vanity): - target = data_store.get(vanity) - if target and target['type'] == 'file': - filename = f'{vanity}_{target["filename"]}' + db = get_db() + cursor = db.cursor() + cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'file'", (vanity,)) + target = cursor.fetchone() + if target: + filename = f'{vanity}_{target[2]}' return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True) return 'Not Found', 404 @@ -50,12 +116,12 @@ def upload_pastebin(): content = request.form['content'] vanity = shortuuid.uuid()[:6] created_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - data_store[vanity] = {'type': 'pastebin', 'content': content, 'created_at': created_at} - - html_content = render_template('content.html', content=content, created_at=created_at) - html_file_path = os.path.join('templates', f'{vanity}.html') - with open(html_file_path, 'w') as f: - f.write(html_content) + + db = get_db() + cursor = db.cursor() + cursor.execute("INSERT INTO content (vanity, type, data, created_at) VALUES (?, ?, ?, ?)", + (vanity, 'pastebin', content, created_at)) + db.commit() return jsonify({'vanity': vanity}) @@ -71,19 +137,13 @@ def upload_file(): filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{filename}') file.save(filepath) - data_store[vanity] = {'type': 'file', 'filename': filename} - - file_info = { - 'name': filename, - 'size': os.path.getsize(filepath), - 'modified_at': datetime.fromtimestamp(os.path.getmtime(filepath)).strftime('%Y-%m-%d %H:%M:%S'), - 'url': url_for('download_file', vanity=vanity) - } - html_content = render_template('file.html', **file_info) - html_file_path = os.path.join('templates', f'{vanity}.html') - with open(html_file_path, 'w') as f: - f.write(html_content) - + + db = get_db() + cursor = db.cursor() + cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", + (vanity, 'file', filename)) + db.commit() + return jsonify({'vanity': vanity}) def save_file(file, folder_path): @@ -115,7 +175,11 @@ def upload_folder(): handle_uploaded_folder(files, folder_path) - data_store[vanity] = {'type': 'folder', 'files': [file.filename for file in files]} + db = get_db() + cursor = db.cursor() + cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", + (vanity, 'folder', ','.join([file.filename for file in files]))) + db.commit() return jsonify({'vanity': vanity}) @@ -123,51 +187,59 @@ def upload_folder(): def shorten_url(): original_url = request.form['url'] vanity = shortuuid.uuid()[:6] - data_store[vanity] = {'type': 'url', 'url': original_url} - - html_content = f'' - html_file_path = os.path.join('templates', f'{vanity}.html') - with open(html_file_path, 'w') as f: - f.write(html_content) + + db = get_db() + cursor = db.cursor() + cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", + (vanity, 'url', original_url)) + db.commit() return jsonify({'vanity': vanity}) @app.route('/', methods=['GET']) def redirect_vanity(vanity): - target = data_store.get(vanity) + db = get_db() + cursor = db.cursor() + cursor.execute("SELECT * FROM content WHERE vanity = ?", (vanity,)) + target = cursor.fetchone() if target: - if target['type'] == 'pastebin': - return render_template(f'{vanity}.html') - elif target['type'] == 'file': - file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{target["filename"]}') + content_type, content_data = target[1], target[2] + if content_type == 'pastebin': + return render_template('content.html', content=content_data, created_at=target[3]) + elif content_type == 'file': + file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{content_data}') file_info = { - 'name': target['filename'], + 'name': content_data, 'size': os.path.getsize(file_path), 'modified_at': datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M:%S'), 'url': url_for('download_file', vanity=vanity) } return render_template('file.html', **file_info) - elif target['type'] == 'folder': + elif content_type == 'folder': return redirect(url_for('folder_content', vanity=vanity)) - elif target['type'] == 'url': - return render_template('content.html', url=target['url']) + elif content_type == 'url': + return render_template('content.html', url=content_data) return render_template('404.html'), 404 @app.route('//raw', methods=['GET']) def raw_vanity(vanity): - target = data_store.get(vanity) + db = get_db() + cursor = db.cursor() + cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'pastebin'", (vanity,)) + target = cursor.fetchone() if target: - if target['type'] == 'pastebin': - return render_template(f'{vanity}raw.html') - - return render_template('404.html'), 404 + return target[2], 200, {'Content-Type': 'text/plain; charset=utf-8'} + return 'Not Found', 404 @app.route('/folder/', methods=['GET']) def folder_content(vanity): - target = data_store.get(vanity) - if target and target['type'] == 'folder': + db = get_db() + cursor = db.cursor() + cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'folder'", (vanity,)) + target = cursor.fetchone() + if target: folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) files = [] for root, _, filenames in os.walk(folder_path): @@ -188,14 +260,27 @@ def folder_content(vanity): prev_url = url_for('folder_content', vanity=vanity, page=page-1) if page > 1 else None next_url = url_for('folder_content', vanity=vanity, page=page+1) if end < total_files else None - return render_template('folder.html', files=files, prev_url=prev_url, next_url=next_url) + download_all_url = url_for('download_folder_as_zip', vanity=vanity) + + # Get the current folder name + current_folder = os.path.basename(folder_path) + + return render_template('folder.html', + files=files, + prev_url=prev_url, + next_url=next_url, + download_all_url=download_all_url, + current_folder=current_folder) return 'Not Found', 404 @app.route('/folder//download', methods=['GET']) def download_folder_as_zip(vanity): - target = data_store.get(vanity) - if target and target['type'] == 'folder': + db = get_db() + cursor = db.cursor() + cursor.execute("SELECT * FROM content WHERE vanity = ? AND type = 'folder'", (vanity,)) + target = cursor.fetchone() + if target: folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) zip_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}.zip') diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..8b044dc --- /dev/null +++ b/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS content ( + vanity TEXT PRIMARY KEY, + type TEXT NOT NULL, + data TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/templates/folder.html b/templates/folder.html index e6b5340..f516f6d 100644 --- a/templates/folder.html +++ b/templates/folder.html @@ -3,37 +3,43 @@ - Folder + Folder Contents
-

Folder

-
diff --git a/uploads/.DS_Store b/uploads/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 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 ZO$Fx9?_0nE-b=Iy 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