diff --git a/app.py b/app.py index 888c667..aa07f16 100644 --- a/app.py +++ b/app.py @@ -1,20 +1,19 @@ -from flask import Flask, request, jsonify, send_from_directory, render_template, url_for, redirect, send_file +from flask import Flask, request, jsonify, send_from_directory, render_template, url_for, redirect, send_file, session from werkzeug.utils import secure_filename import shortuuid import os -from datetime import datetime +from datetime import datetime, timedelta import zipfile import sqlite3 import threading import time import shutil -from datetime import timedelta from pygments import highlight from pygments.lexers import get_lexer_by_name, guess_lexer from pygments.formatters import HtmlFormatter from pygments.util import ClassNotFound import json -from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user +from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user, login_remembered import hashlib app = Flask(__name__) @@ -22,6 +21,7 @@ app.secret_key = 'your_secret_key_here' # Add this line UPLOAD_FOLDER = './uploads' app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER DATABASE = 'data.db' +app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=30) # Set cookie to expire after 30 days if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) @@ -115,146 +115,82 @@ def load_user(user_id): @app.route('/') def index(): - return render_template('index.html') + if current_user.is_authenticated: + return render_template('index.html', user=current_user) + return render_template('index.html', user=None) -@app.route('/content/') -def content(vanity): +@app.route('/u/') +@app.route('/u//') +@app.route('/u//') +def serve_user_page(username, filename=None): + print(f"Accessing user page: {username}, filename: {filename}") # Debug print + + # Check if the username exists in the database db = get_db() cursor = db.cursor() - cursor.execute("SELECT * FROM content WHERE vanity = ?", (vanity,)) - target = cursor.fetchone() + cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) + user = cursor.fetchone() + if not user: + print(f"User {username} not found") # Debug print + return "User not found", 404 + + user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) + print(f"User folder path: {user_folder}") # Debug print - if target: - content_type, content_data = target[1], target[2] - if content_type == 'pastebin': - try: - lexer = guess_lexer(content_data) - language = lexer.aliases[0] - except ClassNotFound: - language = 'text' - lexer = get_lexer_by_name(language) - - formatter = HtmlFormatter(style='monokai', linenos=True, cssclass="source") - highlighted_code = highlight(content_data, lexer, formatter) - css = formatter.get_style_defs('.source') - return render_template('content.html', - highlighted_content=highlighted_code, - css=css, - raw_content=content_data, - created_at=target[3], - vanity=vanity, - language=language) - elif content_type == 'file': - file_path = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{content_data}') - file_info = { - '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 content_type == 'url': - return render_template('content.html', url=content_data) - return 'Not Found', 404 + if not os.path.exists(user_folder): + print(f"User folder does not exist for {username}") # Debug print + os.makedirs(user_folder) # Create the folder if it doesn't exist -@app.route('/download/', methods=['GET']) -def download_file(vanity): - 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 + current_path = os.path.join(user_folder, filename.rstrip('/') if filename else '') + if not os.path.exists(current_path): + return "Folder or file not found", 404 -@app.route('/upload/pastebin', methods=['POST']) -def upload_pastebin(): - content = request.form['content'] - vanity = shortuuid.uuid()[:6] - created_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - - 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}) + if os.path.isfile(current_path): + return send_file(current_path) -@app.route('/upload/file', methods=['POST']) -def upload_file(): - if 'file' not in request.files: - return 'No file part', 400 - file = request.files['file'] - if file.filename == '': - return 'No selected file', 400 - if file: - vanity = shortuuid.uuid()[:6] - filename = secure_filename(file.filename) - filepath = os.path.join(app.config['UPLOAD_FOLDER'], f'{vanity}_{filename}') - file.save(filepath) - - db = get_db() - cursor = db.cursor() - cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", - (vanity, 'file', filename)) - db.commit() - - return jsonify({'vanity': vanity}) + # Check if we should ignore index.html + ignore_index = session.get(f'ignore_index_{username}', False) -def save_file(file, folder_path): - filename = secure_filename(file.filename) - file_path = os.path.join(folder_path, filename) - file.save(file_path) + # Check for index.html + index_path = os.path.join(current_path, 'index.html') + if os.path.exists(index_path) and not ignore_index: + return send_file(index_path) -def handle_uploaded_folder(files, base_path): - for file in files: - if file.filename.endswith('/'): - subfolder_path = os.path.join(base_path, secure_filename(file.filename)) - os.makedirs(subfolder_path, exist_ok=True) - handle_uploaded_folder(request.files.getlist(file.filename), subfolder_path) + # Directory listing + files = [] + folders = [] + for item in os.listdir(current_path): + item_path = os.path.join(current_path, item) + relative_path = os.path.relpath(item_path, user_folder) + if os.path.isfile(item_path): + files.append({'name': item, 'path': relative_path}) else: - save_file(file, base_path) + folders.append({'name': item, 'path': relative_path}) -@app.route('/upload/folder', methods=['POST']) -def upload_folder(): - if 'file' not in request.files: - return 'No files uploaded', 400 - - files = request.files.getlist('file') - if not files: - return 'No files selected', 400 - - vanity = shortuuid.uuid()[:6] - folder_path = os.path.join(app.config['UPLOAD_FOLDER'], vanity) - os.makedirs(folder_path) + parent_folder = os.path.dirname(filename.rstrip('/')) if filename else None + current_folder = os.path.basename(current_path) - handle_uploaded_folder(files, folder_path) - - 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() + # Generate the correct parent folder URL + parent_url = None + if parent_folder: + parent_url = url_for('serve_user_page', username=username, filename=parent_folder) + elif filename: # If we're in a subfolder, parent is the root + parent_url = url_for('serve_user_page', username=username) - return jsonify({'vanity': vanity}) + return render_template('user_files_public.html', + username=username, + files=files, + folders=folders, + current_path=filename.rstrip('/') if filename else '', + parent_url=parent_url, + current_folder=current_folder) -@app.route('/shorten', methods=['POST']) -def shorten_url(): - original_url = request.form['url'] - vanity = shortuuid.uuid()[:6] - - db = get_db() - cursor = db.cursor() - cursor.execute("INSERT INTO content (vanity, type, data) VALUES (?, ?, ?)", - (vanity, 'url', original_url)) - db.commit() +@app.route('/') +def redirect_vanity(path): + parts = path.rstrip('/').split('/') + vanity = parts[0] + subpath = '/'.join(parts[1:]) if len(parts) > 1 else '' - return jsonify({'vanity': vanity}) - -@app.route('/', methods=['GET']) -def redirect_vanity(vanity): db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM content WHERE vanity = ?", (vanity,)) @@ -388,6 +324,12 @@ def register(): cursor.execute("INSERT INTO users (username, password_hash) VALUES (?, ?)", (username, hashed_password)) db.commit() + + # Create user directory + user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) + if not os.path.exists(user_folder): + os.makedirs(user_folder) + return redirect(url_for('login')) return render_template('register.html') @@ -396,12 +338,14 @@ def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] + remember = 'remember' in request.form db = get_db() cursor = db.cursor() cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) user = cursor.fetchone() if user and User.verify_password(user[2], password): - login_user(User(user[0], user[1], user[2])) + user_obj = User(user[0], user[1], user[2]) + login_user(user_obj, remember=remember) return redirect(url_for('user_files', username=username)) return "Invalid username or password" return render_template('login.html') @@ -412,21 +356,74 @@ def logout(): logout_user() return redirect(url_for('index')) -@app.route('/user/') -def user_files(username): - if current_user.is_authenticated and current_user.username == username: - user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) - if not os.path.exists(user_folder): - os.makedirs(user_folder) - files = os.listdir(user_folder) - return render_template('user_files.html', username=username, files=files) - return "Unauthorized", 401 +@app.route('/dash/') +@app.route('/dash//') +@app.route('/dash//') +@login_required +def user_files(username, subpath=''): + if current_user.username != username: + return "Unauthorized", 401 + + user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) + current_path = os.path.join(user_folder, subpath.rstrip('/')) + + # Create user folder if it doesn't exist + if not os.path.exists(user_folder): + os.makedirs(user_folder) + + if not os.path.exists(current_path): + return "Folder not found", 404 + + if not os.path.isdir(current_path): + return "Not a directory", 400 + + items = [] + folders = [] + for item in os.listdir(current_path): + item_path = os.path.join(current_path, item) + relative_path = os.path.relpath(item_path, user_folder) + if os.path.isfile(item_path): + items.append({'name': item, 'type': 'file', 'path': relative_path}) + else: + items.append({'name': item, 'type': 'folder', 'path': relative_path}) + folders.append(relative_path) + + parent_folder = os.path.dirname(subpath.rstrip('/')) if subpath else None + current_folder = os.path.basename(current_path) + + # Check if index.html exists in the current folder + index_exists = 'index.html' in [item['name'] for item in items if item['type'] == 'file'] + + # Get the current setting for ignoring index.html + ignore_index = session.get(f'ignore_index_{username}', False) + + return render_template('user_files.html', + username=username, + items=items, + folders=folders, + current_path=subpath.rstrip('/'), + parent_folder=parent_folder, + current_folder=current_folder, + index_exists=index_exists, + ignore_index=ignore_index) -@app.route('/user//upload', methods=['POST']) +@app.route('/dash//toggle_index') +@login_required +def toggle_index(username): + if current_user.username != username: + return "Unauthorized", 401 + + current_setting = session.get(f'ignore_index_{username}', False) + session[f'ignore_index_{username}'] = not current_setting + + return redirect(url_for('user_files', username=username)) + +@app.route('/dash//upload', methods=['POST']) @login_required def upload_user_file(username): if current_user.username != username: return "Unauthorized", 401 + subpath = request.form.get('subpath', '').rstrip('/') if 'file' not in request.files: return 'No file part', 400 file = request.files['file'] @@ -434,11 +431,12 @@ def upload_user_file(username): return 'No selected file', 400 if file: filename = secure_filename(file.filename) - file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, filename) + file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, subpath, filename) + os.makedirs(os.path.dirname(file_path), exist_ok=True) file.save(file_path) - return redirect(url_for('user_files', username=username)) + return redirect(url_for('user_files', username=username, subpath=subpath)) -@app.route('/user//delete/', methods=['POST']) +@app.route('/dash//delete/', methods=['POST']) @login_required def delete_user_file(username, filename): if current_user.username != username: @@ -448,7 +446,7 @@ def delete_user_file(username, filename): os.remove(file_path) return redirect(url_for('user_files', username=username)) -@app.route('/user//rename', methods=['POST']) +@app.route('/dash//rename', methods=['POST']) @login_required def rename_user_file(username): if current_user.username != username: @@ -461,90 +459,100 @@ def rename_user_file(username): os.rename(old_path, new_path) return redirect(url_for('user_files', username=username)) -@app.route('/') -@app.route('//') -@app.route('//') -def serve_user_page(username, filename=None): - print(f"Accessing user page: {username}, filename: {filename}") # Debug print +@app.route('/dash//create_folder', methods=['POST']) +@login_required +def create_folder(username): + if current_user.username != username: + return "Unauthorized", 401 + subpath = request.form.get('subpath', '').rstrip('/') + folder_name = secure_filename(request.form['folder_name']) + folder_path = os.path.join(app.config['UPLOAD_FOLDER'], username, subpath, folder_name) + if not os.path.exists(folder_path): + os.makedirs(folder_path) + return redirect(url_for('user_files', username=username, subpath=subpath)) - # Check if the username exists in the database - db = get_db() - cursor = db.cursor() - cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) - user = cursor.fetchone() - if not user: - print(f"User {username} not found") # Debug print - return "User not found", 404 +@app.route('/dash//delete_folder/', methods=['POST']) +@login_required +def delete_folder(username, folder_name): + if current_user.username != username: + return "Unauthorized", 401 + folder_path = os.path.join(app.config['UPLOAD_FOLDER'], username, folder_name) + if os.path.exists(folder_path): + shutil.rmtree(folder_path) + return redirect(url_for('user_files', username=username)) - user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) - print(f"User folder path: {user_folder}") # Debug print - - if not os.path.exists(user_folder): - print(f"User folder does not exist for {username}") # Debug print - os.makedirs(user_folder) # Create the folder if it doesn't exist +@app.route('/dash//rename_folder', methods=['POST']) +@login_required +def rename_folder(username): + if current_user.username != username: + return "Unauthorized", 401 + old_foldername = request.form['old_foldername'] + new_foldername = secure_filename(request.form['new_foldername']) + old_path = os.path.join(app.config['UPLOAD_FOLDER'], username, old_foldername) + new_path = os.path.join(app.config['UPLOAD_FOLDER'], username, new_foldername) + if os.path.exists(old_path): + os.rename(old_path, new_path) + return redirect(url_for('user_files', username=username)) - if filename is None or filename == '': - # Try to serve index.html - index_path = os.path.join(user_folder, 'index.html') - print(f"Checking for index.html at: {index_path}") # Debug print - if os.path.exists(index_path): - print(f"Serving index.html for {username}") # Debug print - return send_file(index_path) +@app.route('/dash//move_item', methods=['POST']) +@login_required +def move_item(username): + if current_user.username != username: + return "Unauthorized", 401 + item_name = request.form['item_name'] + item_type = request.form['item_type'] + destination_folder = request.form['destination_folder'] + source_path = os.path.join(app.config['UPLOAD_FOLDER'], username, item_name) + dest_path = os.path.join(app.config['UPLOAD_FOLDER'], username, destination_folder, item_name) + if os.path.exists(source_path): + shutil.move(source_path, dest_path) + return redirect(url_for('user_files', username=username)) + +@app.route('/dash//copy_item', methods=['POST']) +@login_required +def copy_item(username): + if current_user.username != username: + return "Unauthorized", 401 + item_name = request.form['item_name'] + item_type = request.form['item_type'] + destination_folder = request.form['destination_folder'] + source_path = os.path.join(app.config['UPLOAD_FOLDER'], username, item_name) + dest_path = os.path.join(app.config['UPLOAD_FOLDER'], username, destination_folder, item_name) + if os.path.exists(source_path): + if item_type == 'file': + shutil.copy2(source_path, dest_path) else: - print(f"No index.html found, listing files for {username}") # Debug print - # If no index.html, list all files - files = os.listdir(user_folder) - print(f"Files in {username}'s folder: {files}") # Debug print - return render_template('user_files_public.html', username=username, files=files) - else: - # Serve the requested file - file_path = os.path.join(user_folder, filename) - print(f"Attempting to serve file: {file_path}") # Debug print - if os.path.exists(file_path) and os.path.isfile(file_path): - print(f"Serving file: {file_path}") # Debug print - return send_file(file_path) - else: - print(f"File not found: {file_path}") # Debug print - return "File not found", 404 + shutil.copytree(source_path, dest_path) + return redirect(url_for('user_files', username=username)) + +@app.route('/dash//edit/', methods=['GET', 'POST']) +@login_required +def edit_file(username, filename): + if current_user.username != username: + return "Unauthorized", 401 + + file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, filename) + if not os.path.exists(file_path): + return "File not found", 404 + + if request.method == 'POST': + content = request.form['content'] + with open(file_path, 'w') as f: + f.write(content) + return redirect(url_for('user_files', username=username)) + + with open(file_path, 'r') as f: + content = f.read() + + return render_template('edit_file.html', filename=filename, content=content) @app.route('/debug/users') def debug_users(): db = get_db() cursor = db.cursor() - cursor.execute("SELECT username FROM users") + cursor.execute("SELECT * FROM users") users = cursor.fetchall() - - user_files = {} - for user in users: - username = user[0] - user_folder = os.path.join(app.config['UPLOAD_FOLDER'], username) - if os.path.exists(user_folder): - user_files[username] = os.listdir(user_folder) - else: - user_files[username] = [] - - return jsonify(user_files) - -@app.route('/user//edit/', methods=['GET', 'POST']) -@login_required -def edit_file(username, filename): - if current_user.username != username: - return "Unauthorized", 401 - - file_path = os.path.join(app.config['UPLOAD_FOLDER'], username, filename) - - if request.method == 'POST': - content = request.form['content'] - with open(file_path, 'w') as file: - file.write(content) - return redirect(url_for('user_files', username=username)) - - if os.path.exists(file_path) and os.path.isfile(file_path): - with open(file_path, 'r') as file: - content = file.read() - return render_template('edit_file.html', username=username, filename=filename, content=content) - else: - return "File not found", 404 + return jsonify(users) if __name__ == '__main__': - app.run(debug=True, port=7123) + app.run(debug=True) diff --git a/schema.sql b/schema.sql index 8152bd3..8af69c3 100644 --- a/schema.sql +++ b/schema.sql @@ -2,7 +2,8 @@ CREATE TABLE IF NOT EXISTS content ( vanity TEXT PRIMARY KEY, type TEXT NOT NULL, data TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + user_id INTEGER ); CREATE TABLE IF NOT EXISTS users ( diff --git a/templates/content.html b/templates/content.html index fdd94eb..fdb09ef 100644 --- a/templates/content.html +++ b/templates/content.html @@ -154,24 +154,29 @@
-

Content

- {% if created_at %} -

Created at: {{ created_at }}

+

Content

+ {% if content.user_id %} +

Uploaded by: {{ content.username }}

+ {% else %} +

Uploaded by: Anonymous

{% endif %} - {% if language %} -

Detected Language: {{ language }}

+

Created at: {{ content.created_at }}

+ + {% if highlighted_content %} + + {{ highlighted_content|safe }} + {% elif url %} +

Shortened URL: {{ url }}

+ {% else %} +
{{ raw_content }}
+ {% endif %} + + {% if current_user.is_authenticated and current_user.id == content.user_id %} + Edit +
+ +
{% endif %} -
- - -
-
- {% if highlighted_content %} - {{ highlighted_content|safe }} - {% else %} -
{{ content }}
- {% endif %} -
diff --git a/templates/edit_content.html b/templates/edit_content.html new file mode 100644 index 0000000..f9cd465 --- /dev/null +++ b/templates/edit_content.html @@ -0,0 +1,22 @@ + + + + + + Edit Content + + + +
+

Edit Content

+
+ +
+ +
+ Back to Dashboard +
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index de5dfd7..e81a1ed 100644 --- a/templates/index.html +++ b/templates/index.html @@ -112,8 +112,13 @@
diff --git a/templates/login.html b/templates/login.html index 5c73e92..9595b92 100644 --- a/templates/login.html +++ b/templates/login.html @@ -1,6 +1,68 @@ -

Login

-
- - - -
\ No newline at end of file + + + + + + Login + + + + + + \ No newline at end of file diff --git a/templates/register.html b/templates/register.html index 1d563ce..243790d 100644 --- a/templates/register.html +++ b/templates/register.html @@ -1,6 +1,62 @@ -

Register

-
- - - -
\ No newline at end of file + + + + + + Register + + + +
+

Register

+
+ + + +
+
+ + \ No newline at end of file diff --git a/templates/user_files.html b/templates/user_files.html index 3d9612f..65d183d 100644 --- a/templates/user_files.html +++ b/templates/user_files.html @@ -1,21 +1,269 @@ -

{{ username }}'s Files

-
- - -
-
    -{% for file in files %} -
  • - {{ file }} -
    - + + + + + + {{ username }}'s Dashboard + + + +
    +

    {{ username }}'s Dashboard

    + + + + -
    - - - + + {% if index_exists and not ignore_index %} +

    An index.html file exists in this folder. When viewing publicly, this file will be displayed instead of the file listing.

    + {% endif %} + +

    Upload File

    + + + +
    - Edit -
  • -{% endfor %} -
\ No newline at end of file + +

Create Folder

+
+ + + +
+ +

Files and Folders

+

Current folder: {{ current_folder or 'Root' }}

+ +
    + {% if parent_folder is not none %} +
  • + 📁 + + .. + +
    + +
    +
  • + {% endif %} + {% for item in items %} +
  • + {% if item.type == 'folder' %}📁{% else %}📄{% endif %} + + {% if item.type == 'folder' %} + {{ item.name }} + {% else %} + {{ item.name }} + {% endif %} + +
    + + + + + {% if item.type == 'file' %} + Edit + {% endif %} +
    +
  • + {% endfor %} +
+ +

Your Uploads

+
    + {% for upload in uploads %} +
  • + + {% if upload.type == 'pastebin' %}📝 + {% elif upload.type == 'file' %}📄 + {% elif upload.type == 'folder' %}📁 + {% elif upload.type == 'url' %}🔗 + {% endif %} + + + {{ upload.vanity }} + ({{ upload.type }}) + +
    + {% if upload.type in ['pastebin', 'url'] %} + Edit + {% endif %} +
    + +
    +
    +
  • + {% endfor %} +
+
+ + + + \ No newline at end of file diff --git a/templates/user_files_public.html b/templates/user_files_public.html index 24c16e5..dd55f3f 100644 --- a/templates/user_files_public.html +++ b/templates/user_files_public.html @@ -16,12 +16,19 @@ h2 { color: #4CAF50; } - ul { - list-style-type: none; + .file-list { + display: flex; + flex-wrap: wrap; + gap: 10px; padding: 0; + list-style-type: none; } - li { - margin-bottom: 10px; + .file-item { + background-color: #2a2a2a; + padding: 10px; + border-radius: 5px; + display: flex; + align-items: center; } a { color: #4CAF50; @@ -30,13 +37,34 @@ a:hover { text-decoration: underline; } + .folder { + font-weight: bold; + } + .file-icon { + margin-right: 5px; + }

{{ username }}'s Files

-
    +

    Current folder: {{ current_folder or 'Root' }}

    + + {% if parent_url is not none %} +

    Parent Directory

    + {% endif %} + + diff --git a/uploads/asdasd/index.html b/uploads/asdasd/index.html deleted file mode 100644 index 3943ef1..0000000 --- a/uploads/asdasd/index.html +++ /dev/null @@ -1,27 +0,0 @@ -Python IDE

    Python IDE