diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/app.cpython-314.pyc b/__pycache__/app.cpython-314.pyc new file mode 100644 index 0000000..0329cb0 Binary files /dev/null and b/__pycache__/app.cpython-314.pyc differ diff --git a/__pycache__/config.cpython-314.pyc b/__pycache__/config.cpython-314.pyc new file mode 100644 index 0000000..5c7562d Binary files /dev/null and b/__pycache__/config.cpython-314.pyc differ diff --git a/__pycache__/extensions.cpython-314.pyc b/__pycache__/extensions.cpython-314.pyc new file mode 100644 index 0000000..117a04e Binary files /dev/null and b/__pycache__/extensions.cpython-314.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..88d7af1 --- /dev/null +++ b/app.py @@ -0,0 +1,39 @@ +import os +import logging +from flask import Flask +from config import Config +from extensions import db # db is now imported from extensions.py +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +def create_app(): + app = Flask(__name__) + app.config.from_object(Config) + + # Initialize extensions + db.init_app(app) + + # Logging setup + os.makedirs(app.config['LOG_FOLDER'], exist_ok=True) + logging.basicConfig( + filename=f"{app.config['LOG_FOLDER']}/app.log", + level=logging.INFO, + format='%(asctime)s [%(levelname)s] %(message)s' + ) + + # Register blueprints + from controllers.file_controller import file_bp + app.register_blueprint(file_bp) + + # Create database tables + with app.app_context(): + db.create_all() + + return app + +# Run the app +if __name__ == '__main__': + app = create_app() + app.run(debug=True) diff --git a/config.py b/config.py new file mode 100644 index 0000000..9e64997 --- /dev/null +++ b/config.py @@ -0,0 +1,14 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +class Config: + SQLALCHEMY_DATABASE_URI = ( + f"mysql+pymysql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@" + f"{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('DB_NAME')}" + ) + SQLALCHEMY_TRACK_MODIFICATIONS = False + UPLOAD_FOLDER = os.getenv('UPLOAD_FOLDER', 'uploads') + LOG_FOLDER = 'logs' + DEBUG = True \ No newline at end of file diff --git a/controllers/__pycache__/file_controller.cpython-314.pyc b/controllers/__pycache__/file_controller.cpython-314.pyc new file mode 100644 index 0000000..3256c41 Binary files /dev/null and b/controllers/__pycache__/file_controller.cpython-314.pyc differ diff --git a/controllers/file_controller.py b/controllers/file_controller.py new file mode 100644 index 0000000..727fe3b --- /dev/null +++ b/controllers/file_controller.py @@ -0,0 +1,61 @@ +from flask import Blueprint, request, jsonify, send_file, current_app +from services.file_service import save_file, update_file, delete_file +from models.uploaded_file import UploadedFile +import uuid +import logging + +file_bp = Blueprint('file_bp', __name__) + +@file_bp.route('/upload', methods=['POST']) +def upload_file(): + try: + if 'file' not in request.files: + return jsonify({'message': 'No file uploaded'}), 400 + + file = request.files['file'] + folder = f"{current_app.config['UPLOAD_FOLDER']}/{uuid.uuid4().hex}" + upload_file = save_file(file, folder) + + return jsonify({'message': 'Upload successfull', 'id': upload_file.id, 'path': upload_file.file_path}), 201 + + except Exception as e: + logging.error(f"Upload error: {e}") + return jsonify({'message': str(e)}), 400 + + +@file_bp.route('/file/', methods=['GET']) +def get_file(file_id): + try: + file = UploadedFile.query.get(file_id) + if not file: + return jsonify({'message': 'File not found'}), 404 + + return send_file(file.file_path, as_attachment=True) + except Exception as e: + logging.error(f"Get error: {e}") + return jsonify({'message': str(e)}), 400 + + +@file_bp.route('/file/', methods=['PUT']) +def update_existing_file(file_id): + try: + if 'file' not in request.files: + return jsonify({'message': 'No file provided'}), 400 + + file = request.files['file'] + updated = update_file(file_id, file, current_app.config['UPLOAD_FOLDER']) + + return jsonify({'message': 'File updated successfully', 'path': updated.file_path}), 200 + + except Exception as e: + logging.error(f"Update error: {e}") + return jsonify({"message": str(e)}), 400 + +@file_bp.route('/file/', methods=['DELETE']) +def delete_existing_file(file_id): + try: + deleted = delete_file(file_id) + return jsonify({'message': 'File deleted successfully'}), 200 + except Exception as e: + logging.error(f"Delete error: {e}") + return jsonify({'message': str(e)}), 400 diff --git a/extensions.py b/extensions.py new file mode 100644 index 0000000..f0b13d6 --- /dev/null +++ b/extensions.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() diff --git a/logs/app.log b/logs/app.log new file mode 100644 index 0000000..b9eb509 --- /dev/null +++ b/logs/app.log @@ -0,0 +1,90 @@ +2025-10-19 00:24:29,307 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 00:24:29,308 [INFO] Press CTRL+C to quit +2025-10-19 00:24:29,315 [INFO] * Restarting with stat +2025-10-19 00:24:31,078 [WARNING] * Debugger is active! +2025-10-19 00:24:31,094 [INFO] * Debugger PIN: 311-512-146 +2025-10-19 00:27:10,978 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 00:27:10,978 [INFO] Press CTRL+C to quit +2025-10-19 00:27:10,983 [INFO] * Restarting with stat +2025-10-19 00:27:12,750 [WARNING] * Debugger is active! +2025-10-19 00:27:12,779 [INFO] * Debugger PIN: 384-618-670 +2025-10-19 00:29:28,633 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:29:28] code 400, message Bad request version ('\x8d\x19¦\x92@pÀ泺åÕö\x83\x02,x~\x8cr%') +2025-10-19 00:29:28,641 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:29:28] code 400, message Bad request version ('*') +2025-10-19 00:29:28,662 [INFO] 127.0.0.1 - - [19/Oct/2025 00:29:28] "\x16\x03\x01\x07\x12\x01\x00\x07\x0e\x03\x03W\x94ihYnÍ\x07øë\x1bÅÔz!´5rVî¼Ò\x88\x01Ô\x1eCÐ\x06¼\x0b* " 400 - +2025-10-19 00:29:28,661 [INFO] 127.0.0.1 - - [19/Oct/2025 00:29:28] "\x16\x03\x01\x06ò\x01\x00\x06î\x03\x03¡Ð91À,\x1bi\x17&¢¨\x1ezßµÇVÅ@\x1fqéº9ÿiPYÔI\x8b ÃMÄÖ!së\x9eö£ô0ä\x88\x0bZ\x82Ë\x83þ\x9eQ\x90\x15·\x05£§\x0c¢ý¯\x00 zz\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x06\x85ÚÚ\x00\x00Di\x00\x05\x00\x03\x02h2ÿ\x01\x00\x01\x00\x00#\x00\x00\x00\x12\x00\x00þ\x0d\x00ú\x00\x00\x01\x00\x01\x17\x00 5|6\x16æõ\x9fºØ$\x00Ðá\x00Xq.ÉÚ3KKW\x89-tõþÊüÖá\x1d`Ã$Jì\x8e\x8bP­\x1a´h\x96¨6\x8fZVÄC/k}höN­j'³¨?\x19Fa\\£Xþ\x93öÞ²ìÕE¿5o¿j:\x92Ë\x03GÊ]þ='*\x12\x18"/©Yu\x1f\x8d\x19¦\x92@pÀ泺åÕö\x83\x02,x~\x8cr%" 400 - +2025-10-19 00:29:28,709 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:29:28] code 400, message Bad request version ('\x8a\x8a\x00\x00\x00') +2025-10-19 00:29:28,709 [INFO] 127.0.0.1 - - [19/Oct/2025 00:29:28] "\x16\x03\x01\x06ò\x01\x00\x06î\x03\x03±\x9caë²xi\x97>óU.\x97ù½\x08õõ%¡ Üg\x16º\x8fä¯ñ¡ëH ³\x97 þ\x14\x9f\x06\x0b\x09ËÍ\x17U#Þ\x84æª\x02¤)Ä«ÍÂÁéi¦¦Ù{\x00 jj\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x06\x85\x8a\x8a\x00\x00\x00" 400 - +2025-10-19 00:29:28,991 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:29:28] code 400, message Bad request version ('\x02h2\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00#\x00\x00ÿ\x01\x00\x01\x00\x00') +2025-10-19 00:29:28,991 [INFO] 127.0.0.1 - - [19/Oct/2025 00:29:28] "\x16\x03\x01\x06²\x01\x00\x06®\x03\x03ÚÊ"U7R6e\x1f¦p.í:í2\x82qç\x9f«\x7f­¸Y°E\x06ʯ\x1c\x84 vߨ\x87\x9aÂø¬à¡;gäV)\x7fõp\x84\x82¯Ñ\x92\x1d ïÇu¸eC©\x00 ºº\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x06Eêê\x00\x00\x00\x17\x00\x00\x00\x10\x00\x0e\x00\x0c\x02h2\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00#\x00\x00ÿ\x01\x00\x01\x00\x00" 400 - +2025-10-19 00:29:28,999 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:29:28] code 400, message Bad request version ("Ĭy\x88DTJr\x82¦a°'\x137¹") +2025-10-19 00:29:29,014 [INFO] 127.0.0.1 - - [19/Oct/2025 00:29:29] "\x16\x03\x01\x06ò\x01\x00\x06î\x03\x03ó\x15f|\x0eÍL\x1bÞ\x1e.VÔ\x91\x08\x10訯U%>\x1fÖµf=¤àÐ# ÷z\x8a[Ũ\x81ô.\x16g\x8a\x15\x96\x99nê+RRN\x1a}\x9d\x95m\x9fU$q\x15G\x00 \x8a\x8a\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x06\x85::\x00\x00þ\x0d\x00ú\x00\x00\x01\x00\x01\x16\x00 B©3ÏOß5R¹n F̲{îx\x00a}Ü\x9aí\x0bDC °\x18F×\x1a\x00Ð~$ö\x05Î\x9dç\x9fIÂîï]\x1aïñ\x9bhÇÝ\x83ëù\x126¸n\x08ü\x1dx\x05\x7f\x06¤æ#\x09Ô\\O6 \x8e úµ\x98âÈkc\x0dá¹´Ün]s9Ki\x8d\x00 b\x0b=\x99½ø?¼y\x08~ý7ÝM°\x8eO°¾\x91,YäyÌSëHo\x92\x15Ã'¶¯+%íñß\x0d4ç½ýÆN\x1aðµ*\x04G7ñç!~\x96\x0f\x82Ð6I\x88wO5µ¼\x0bÞÝt¾íò½Ì7--ÃÄÅÍF\x90\x10 \x07B¶\x87\x05´ìak¡È\x1d\x81RU\x1cÍ\x93\x9e\x1c=ÆðÅç«\x94ãâ\x15¡\x9eL¶\x99×W\x0ei/*\x1bY\x89mÿÚ©6P\x17\x00#\x00\x00\x00\x17\x00\x00\x003\x04ï\x04ízz\x00\x01\x00\x11ì\x04ÀÞE\x0bVV1\x8bU\x8e"g^\x17¹\x9e¶@\x87!ç\x09\x88É&úe'Ê\x85\x86!ä#\x0c\x09f_ZT\\{\x19ÇÖ+\x9f\x96#\x1aÆi¤\x89vÌÄd|×vöëF\x87°\x9fíׯ\\6\x88¦k\x84Ep\x0e\x14«º\x1eĬy\x88DTJr\x82¦a°'\x137¹" 400 - +2025-10-19 00:29:29,026 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:29:29] code 400, message Bad request version ('localhost\x00\x12\x00\x00ÿ\x01\x00\x01\x00Di\x00\x05\x00\x03\x02h2\x00') +2025-10-19 00:29:29,026 [INFO] 127.0.0.1 - - [19/Oct/2025 00:29:29] "\x16\x03\x01\x06Ò\x01\x00\x06Î\x03\x03ü2\x03q;Õâ\x01Å»Î.¥å4séÍ*±\x06V÷AÍ·ò\x00\x118à\x15 \x97»Ý\x90øU§lwǼ6\x8fĸ\x09\x9a1\x18\x1en\x10\x95\x0c+~\x12g"zÊÚ\x93 ð2¥³\x8aF\x8a\x08ËUJÈ\x1d\x91»Ì\x9f¥\x12l#\x12ijá/K\x86ËKů\x00 êê\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x06Eªª\x00\x00\x00\x1b\x00\x03\x02\x00\x02\x00\x10\x00\x0e\x00\x0c\x02h2\x08http/1.1ÿ\x01\x00\x01\x00\x00-\x00\x02\x01\x01\x00\x00\x00\x0e\x00\x0c\x00\x00\x09localhost\x00" 400 - +2025-10-19 00:48:42,731 [ERROR] 127.0.0.1 - - [19/Oct/2025 00:48:42] code 400, message Bad request version ('\x02h2\x08http/1.1\x00\x12\x00\x00\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00+\x00\x07\x06úú\x03\x04\x03\x03\x003\x04ï\x04í') +2025-10-19 00:48:42,738 [INFO] 127.0.0.1 - - [19/Oct/2025 00:48:42] "\x16\x03\x01\x06ò\x01\x00\x06î\x03\x03ÞÀwµÙ\x80p\x87¡Q\x99R\x03]Vï\x09Û-s\x1dLÔ³¬\x9f+Ó\x1e\x99Ó\x03 m\x05kC76`Ê>·é=vßÅ\x1c¤\x19{.Ï\x94ZBWºà¦6´}ô\x00 ºº\x13\x01\x13\x02\x13\x03À+À/À,À0̨̩À\x13À\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x06\x85úú\x00\x00\x00-\x00\x02\x01\x01ÿ\x01\x00\x01\x00\x00\x1b\x00\x03\x02\x00\x02\x00\x00\x00\x0e\x00\x0c\x00\x00\x09localhostþ\x0d\x00ú\x00\x00\x01\x00\x01p\x00 ¨\x00\x00\x02i|r{.¼Ó¯/Ý©]!Q\x0fÖOG-¼\x1a\x88}\x98=\x89\x9e\x0b\x00д\x95\x82#Èô­`WL=Ó\x9cx>¸\x1b·£ñ\x86¯Äk«D˲>\x18Õ¢