diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d19ec7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv +.env \ No newline at end of file diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000..bd44269 Binary files /dev/null and b/__pycache__/app.cpython-313.pyc differ diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..f2a8cbb Binary files /dev/null and b/__pycache__/config.cpython-313.pyc differ diff --git a/__pycache__/extensions.cpython-313.pyc b/__pycache__/extensions.cpython-313.pyc new file mode 100644 index 0000000..3d53a8b Binary files /dev/null and b/__pycache__/extensions.cpython-313.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..7dac62e --- /dev/null +++ b/app.py @@ -0,0 +1,35 @@ +import os +import logging +from flask import Flask +from config import Config +from extensions import db # ✅ import shared db here + +def create_app(): + app = Flask(__name__) + app.config.from_object(Config) + + # Initialize extensions + db.init_app(app) # ✅ binds db to this Flask app + + # Configure logging + 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' + ) + + # Import blueprints after db setup + from controllers.file_controller import file_bp + app.register_blueprint(file_bp) + + # Create tables + with app.app_context(): + db.create_all() + + return 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..cff0b87 --- /dev/null +++ b/config.py @@ -0,0 +1,25 @@ +import os +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +class Config: + # ✅ Correct MySQL connection string format + 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')}" + ) + + # ✅ Disable SQLAlchemy event system overhead + SQLALCHEMY_TRACK_MODIFICATIONS = False + + # ⚠️ Fixed typo: "UPLOAD_FOLER" → "UPLOAD_FOLDER" + # Also set a fallback path (e.g., "uploads") + UPLOAD_FOLDER = os.getenv('UPLOAD_FOLDER', 'uploads') + + # ✅ Set default logging directory + LOG_FOLDER = os.getenv('LOG_FOLDER', 'logs') + + # ✅ Enable debug mode if specified, otherwise False + DEBUG = os.getenv('FLASK_DEBUG', 'True').lower() in ('true', '1', 'yes') diff --git a/controllers/__pycache__/file_controller.cpython-313.pyc b/controllers/__pycache__/file_controller.cpython-313.pyc new file mode 100644 index 0000000..c597f39 Binary files /dev/null and b/controllers/__pycache__/file_controller.cpython-313.pyc differ diff --git a/controllers/file_controller.py b/controllers/file_controller.py new file mode 100644 index 0000000..38df03f --- /dev/null +++ b/controllers/file_controller.py @@ -0,0 +1,70 @@ +import uuid +from flask import Blueprint, request, jsonify, send_file +from services.file_service import save_file, update_file, delete_file +from models.uploaded_file import UploadedFile +from flask import Blueprint, request, jsonify, send_file, current_app +import logging + +file_bp = Blueprint('file_bp', __name__) + +# 🟢 POST — Upload file +@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}" + uploaded = save_file(file, folder) + return jsonify({'message': 'Upload successful', 'id': uploaded.id, 'path': uploaded.file_path}), 201 + + except Exception as e: + logging.error(f"Upload error: {e}") + return jsonify({'message': str(e)}), 400 + + +# 🟡 GET — Retrieve file info or download +@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 + + # You can test using Postman → GET http://localhost:5000/file/ + 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 + +# 🟠 PUT — Update file +@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 + +# 🔴 DELETE — Delete file +@file_bp.route('/file/', methods=['DELETE']) +def delete_existing_file(file_id): + try: + 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 \ No newline at end of file diff --git a/extensions.py b/extensions.py new file mode 100644 index 0000000..a8390fa --- /dev/null +++ b/extensions.py @@ -0,0 +1,5 @@ +# extensions.py +from flask_sqlalchemy import SQLAlchemy + +# Single shared SQLAlchemy instance +db = SQLAlchemy() diff --git a/logs/app.log b/logs/app.log new file mode 100644 index 0000000..30d9b3e --- /dev/null +++ b/logs/app.log @@ -0,0 +1,148 @@ +2025-10-19 20:27:54,937 [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 20:27:54,937 [INFO] Press CTRL+C to quit +2025-10-19 20:28:18,581 [INFO] 127.0.0.1 - - [19/Oct/2025 20:28:18] "GET / HTTP/1.1" 404 - +2025-10-19 20:28:18,645 [INFO] 127.0.0.1 - - [19/Oct/2025 20:28:18] "GET /favicon.ico HTTP/1.1" 404 - +2025-10-19 20:28:24,008 [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 20:28:24,008 [INFO] Press CTRL+C to quit +2025-10-19 20:28:33,931 [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 20:28:33,932 [INFO] Press CTRL+C to quit +2025-10-19 20:30:26,578 [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 20:30:26,579 [INFO] Press CTRL+C to quit +2025-10-19 20:36:57,756 [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 20:36:57,756 [INFO] Press CTRL+C to quit +2025-10-19 20:36:57,757 [INFO] * Restarting with stat +2025-10-19 20:36:58,481 [WARNING] * Debugger is active! +2025-10-19 20:36:58,484 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:37:12,841 [INFO] 127.0.0.1 - - [19/Oct/2025 20:37:12] "GET / HTTP/1.1" 404 - +2025-10-19 20:37:15,626 [INFO] 127.0.0.1 - - [19/Oct/2025 20:37:15] "GET /info HTTP/1.1" 404 - +2025-10-19 20:37:20,569 [INFO] 127.0.0.1 - - [19/Oct/2025 20:37:20] "GET /info HTTP/1.1" 404 - +2025-10-19 20:37:51,421 [INFO] 127.0.0.1 - - [19/Oct/2025 20:37:51] "GET /upload HTTP/1.1" 405 - +2025-10-19 20:38:15,012 [ERROR] Upload error: name 'app' is not defined +2025-10-19 20:38:15,013 [INFO] 127.0.0.1 - - [19/Oct/2025 20:38:15] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:40:05,751 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\controllers\\file_controller.py', reloading +2025-10-19 20:40:05,865 [INFO] * Restarting with stat +2025-10-19 20:40:06,796 [WARNING] * Debugger is active! +2025-10-19 20:40:06,799 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:40:11,979 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\controllers\\file_controller.py', reloading +2025-10-19 20:40:12,084 [INFO] * Restarting with stat +2025-10-19 20:40:16,288 [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 20:40:16,288 [INFO] Press CTRL+C to quit +2025-10-19 20:40:16,290 [INFO] * Restarting with stat +2025-10-19 20:40:17,037 [WARNING] * Debugger is active! +2025-10-19 20:40:17,040 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:40:19,334 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 20:40:19,334 [INFO] 127.0.0.1 - - [19/Oct/2025 20:40:19] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:40:44,268 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\app.py', reloading +2025-10-19 20:40:44,401 [INFO] * Restarting with stat +2025-10-19 20:40:45,110 [WARNING] * Debugger is active! +2025-10-19 20:40:45,112 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:40:47,214 [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 20:40:47,214 [INFO] Press CTRL+C to quit +2025-10-19 20:40:47,215 [INFO] * Restarting with stat +2025-10-19 20:40:47,916 [WARNING] * Debugger is active! +2025-10-19 20:40:47,919 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:40:56,168 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\models\\uploaded_file.py', reloading +2025-10-19 20:40:56,270 [INFO] * Restarting with stat +2025-10-19 20:40:57,028 [WARNING] * Debugger is active! +2025-10-19 20:40:57,030 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:41:02,408 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 20:41:02,408 [INFO] 127.0.0.1 - - [19/Oct/2025 20:41:02] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:41:03,492 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 20:41:03,493 [INFO] 127.0.0.1 - - [19/Oct/2025 20:41:03] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:41:24,425 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\__init__.py', reloading +2025-10-19 20:41:24,545 [INFO] * Restarting with stat +2025-10-19 20:41:25,287 [WARNING] * Debugger is active! +2025-10-19 20:41:25,289 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:41:27,688 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\app.py', reloading +2025-10-19 20:41:27,830 [INFO] * Restarting with stat +2025-10-19 20:41:28,583 [WARNING] * Debugger is active! +2025-10-19 20:41:28,586 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:41:30,220 [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 20:41:30,220 [INFO] Press CTRL+C to quit +2025-10-19 20:41:30,221 [INFO] * Restarting with stat +2025-10-19 20:41:30,894 [WARNING] * Debugger is active! +2025-10-19 20:41:30,896 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:41:32,813 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 20:41:32,813 [INFO] 127.0.0.1 - - [19/Oct/2025 20:41:32] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:41:40,769 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\app.py', reloading +2025-10-19 20:41:40,879 [INFO] * Restarting with stat +2025-10-19 20:41:41,672 [WARNING] * Debugger is active! +2025-10-19 20:41:41,674 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:42:00,185 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\config.py', reloading +2025-10-19 20:42:00,290 [INFO] * Restarting with stat +2025-10-19 20:42:01,026 [WARNING] * Debugger is active! +2025-10-19 20:42:01,028 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:42:02,820 [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 20:42:02,833 [INFO] Press CTRL+C to quit +2025-10-19 20:42:02,834 [INFO] * Restarting with stat +2025-10-19 20:42:03,538 [WARNING] * Debugger is active! +2025-10-19 20:42:03,541 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:42:08,910 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 20:42:08,911 [INFO] 127.0.0.1 - - [19/Oct/2025 20:42:08] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:42:09,766 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 20:42:09,766 [INFO] 127.0.0.1 - - [19/Oct/2025 20:42:09] "POST /upload HTTP/1.1" 400 - +2025-10-19 20:42:52,811 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\extensions.py', reloading +2025-10-19 20:42:52,907 [INFO] * Restarting with stat +2025-10-19 20:42:53,689 [WARNING] * Debugger is active! +2025-10-19 20:42:53,691 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:42:56,066 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\app.py', reloading +2025-10-19 20:42:56,180 [INFO] * Restarting with stat +2025-10-19 20:42:56,870 [WARNING] * Debugger is active! +2025-10-19 20:42:56,872 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:43:00,870 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\models\\uploaded_file.py', reloading +2025-10-19 20:43:00,982 [INFO] * Restarting with stat +2025-10-19 20:43:01,658 [WARNING] * Debugger is active! +2025-10-19 20:43:01,660 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:43:03,863 [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 20:43:03,863 [INFO] Press CTRL+C to quit +2025-10-19 20:43:03,865 [INFO] * Restarting with stat +2025-10-19 20:43:04,496 [WARNING] * Debugger is active! +2025-10-19 20:43:04,498 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:43:09,374 [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 20:43:09,374 [INFO] Press CTRL+C to quit +2025-10-19 20:43:09,375 [INFO] * Restarting with stat +2025-10-19 20:43:10,140 [WARNING] * Debugger is active! +2025-10-19 20:43:10,142 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:43:10,721 [INFO] File saved: uploads/193c202f11214be3993a2069f3f64b11\a0cac6305fbe4037b993ef66d4c76e51_Group_1857.png +2025-10-19 20:43:10,724 [INFO] 127.0.0.1 - - [19/Oct/2025 20:43:10] "POST /upload HTTP/1.1" 201 - +2025-10-19 20:44:02,894 [INFO] 127.0.0.1 - - [19/Oct/2025 20:44:02] "GET /file/1 HTTP/1.1" 200 - +2025-10-19 20:44:04,816 [INFO] 127.0.0.1 - - [19/Oct/2025 20:44:04] "GET /file/2 HTTP/1.1" 404 - +2025-10-19 20:44:06,667 [INFO] 127.0.0.1 - - [19/Oct/2025 20:44:06] "GET /file/1 HTTP/1.1" 200 - +2025-10-19 20:44:24,236 [ERROR] Update error: name 'app' is not defined +2025-10-19 20:44:24,236 [INFO] 127.0.0.1 - - [19/Oct/2025 20:44:24] "PUT /file/1 HTTP/1.1" 400 - +2025-10-19 20:44:44,337 [ERROR] Update error: name 'app' is not defined +2025-10-19 20:44:44,337 [INFO] 127.0.0.1 - - [19/Oct/2025 20:44:44] "PUT /file/1 HTTP/1.1" 400 - +2025-10-19 20:45:17,046 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\controllers\\file_controller.py', reloading +2025-10-19 20:45:17,165 [INFO] * Restarting with stat +2025-10-19 20:45:18,995 [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 20:45:18,995 [INFO] Press CTRL+C to quit +2025-10-19 20:45:18,996 [INFO] * Restarting with stat +2025-10-19 20:45:19,668 [WARNING] * Debugger is active! +2025-10-19 20:45:19,670 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:45:21,695 [INFO] File saved: uploads\e491524ff26f42ca911ac06bf4e6c1f9_24SDFDSF.png +2025-10-19 20:45:21,702 [INFO] File updated: ID=1 +2025-10-19 20:45:21,704 [INFO] 127.0.0.1 - - [19/Oct/2025 20:45:21] "PUT /file/1 HTTP/1.1" 200 - +2025-10-19 20:45:29,079 [INFO] * Detected change in 'C:\\Users\\Ginand\\Documents\\Proglan\\file-upload\\controllers\\file_controller.py', reloading +2025-10-19 20:45:29,180 [INFO] * Restarting with stat +2025-10-19 20:45:29,863 [WARNING] * Debugger is active! +2025-10-19 20:45:29,865 [INFO] * Debugger PIN: 881-545-031 +2025-10-19 20:45:32,003 [INFO] 127.0.0.1 - - [19/Oct/2025 20:45:32] "GET /file/1 HTTP/1.1" 200 - +2025-10-19 20:45:40,098 [INFO] File deleted from storage: uploads\e491524ff26f42ca911ac06bf4e6c1f9_24SDFDSF.png +2025-10-19 20:45:40,104 [INFO] Record deleted: ID=1 +2025-10-19 20:45:40,105 [INFO] 127.0.0.1 - - [19/Oct/2025 20:45:40] "DELETE /file/1 HTTP/1.1" 200 - +2025-10-19 20:45:43,580 [INFO] 127.0.0.1 - - [19/Oct/2025 20:45:43] "GET /file/1 HTTP/1.1" 404 - +2025-10-19 20:45:48,919 [INFO] 127.0.0.1 - - [19/Oct/2025 20:45:48] "POST /file HTTP/1.1" 404 - +2025-10-19 20:45:57,444 [INFO] File saved: uploads/16da22714b6e48c7855fb6f2e3035742\a1c64dfdddbf48e7b5634925b0df4811_24SDFDSF.png +2025-10-19 20:45:57,447 [INFO] 127.0.0.1 - - [19/Oct/2025 20:45:57] "POST /upload HTTP/1.1" 201 - diff --git a/models/__pycache__/uploaded_file.cpython-313.pyc b/models/__pycache__/uploaded_file.cpython-313.pyc new file mode 100644 index 0000000..1ad7dfc Binary files /dev/null and b/models/__pycache__/uploaded_file.cpython-313.pyc differ diff --git a/models/uploaded_file.py b/models/uploaded_file.py new file mode 100644 index 0000000..338f18c --- /dev/null +++ b/models/uploaded_file.py @@ -0,0 +1,6 @@ +from extensions import db + +class UploadedFile(db.Model): + id = db.Column(db.Integer, primary_key=True) + filename = db.Column(db.String(255), nullable=False) + file_path = db.Column(db.String(255), nullable=False) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..156b641 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,12 @@ +blinker==1.9.0 +click==8.3.0 +Flask==3.1.2 +Flask-SQLAlchemy==3.1.1 +itsdangerous==2.2.0 +Jinja2==3.1.6 +MarkupSafe==3.0.3 +PyMySQL==1.1.2 +python-dotenv==1.1.1 +SQLAlchemy==2.0.44 +typing_extensions==4.15.0 +Werkzeug==3.1.3 diff --git a/services/__pycache__/file_service.cpython-313.pyc b/services/__pycache__/file_service.cpython-313.pyc new file mode 100644 index 0000000..0f49435 Binary files /dev/null and b/services/__pycache__/file_service.cpython-313.pyc differ diff --git a/services/file_service.py b/services/file_service.py new file mode 100644 index 0000000..094921a --- /dev/null +++ b/services/file_service.py @@ -0,0 +1,49 @@ +import os +import uuid +from werkzeug.utils import secure_filename +from models.uploaded_file import UploadedFile +from app import db +import logging +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'pdf', 'txt'} +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS +def save_file(file, base_folder): + if not allowed_file(file.filename): + raise ValueError('File type not allowed') + + os.makedirs(base_folder, exist_ok=True) + filename = secure_filename(file.filename) + unique_name = f"{uuid.uuid4().hex}_{filename}" + save_path = os.path.join(base_folder, unique_name) + file.save(save_path) + new_file = UploadedFile(filename=unique_name, file_path=save_path) + db.session.add(new_file) + db.session.commit() + logging.info(f"File saved: {save_path}") + return new_file + +def update_file(file_id, new_file, base_folder): + existing = UploadedFile.query.get(file_id) + if not existing: + raise ValueError('File not found') + if os.path.exists(existing.file_path): + os.remove(existing.file_path) + + new_record = save_file(new_file, base_folder) + existing.filename = new_record.filename + existing.file_path = new_record.file_path + db.session.commit() + logging.info(f"File updated: ID={file_id}") + return existing + +def delete_file(file_id): + file_record = UploadedFile.query.get(file_id) + if not file_record: + raise ValueError('File not found') + if os.path.exists(file_record.file_path): + os.remove(file_record.file_path) + logging.info(f"File deleted from storage: {file_record.file_path}") + + db.session.delete(file_record) + db.session.commit() + logging.info(f"Record deleted: ID={file_id}") \ No newline at end of file diff --git a/uploads/04840ee4ba9b48bb8d4a8801ab646c25/9b15f5a5493e4ea0808db5cd9efdba5d_Group_1857.png b/uploads/04840ee4ba9b48bb8d4a8801ab646c25/9b15f5a5493e4ea0808db5cd9efdba5d_Group_1857.png new file mode 100644 index 0000000..30eba96 Binary files /dev/null and b/uploads/04840ee4ba9b48bb8d4a8801ab646c25/9b15f5a5493e4ea0808db5cd9efdba5d_Group_1857.png differ diff --git a/uploads/16da22714b6e48c7855fb6f2e3035742/a1c64dfdddbf48e7b5634925b0df4811_24SDFDSF.png b/uploads/16da22714b6e48c7855fb6f2e3035742/a1c64dfdddbf48e7b5634925b0df4811_24SDFDSF.png new file mode 100644 index 0000000..561875d Binary files /dev/null and b/uploads/16da22714b6e48c7855fb6f2e3035742/a1c64dfdddbf48e7b5634925b0df4811_24SDFDSF.png differ diff --git a/uploads/39ef690cba1d40149c5f7f6d02479fc1/13fe5d81cd3c4822a09fa5cd969f44f7_Group_1857.png b/uploads/39ef690cba1d40149c5f7f6d02479fc1/13fe5d81cd3c4822a09fa5cd969f44f7_Group_1857.png new file mode 100644 index 0000000..30eba96 Binary files /dev/null and b/uploads/39ef690cba1d40149c5f7f6d02479fc1/13fe5d81cd3c4822a09fa5cd969f44f7_Group_1857.png differ diff --git a/uploads/3df4306e78b047aab20c9204cdbfa18b/477327bb79da4a1a9b211f8875515c2d_Group_1857.png b/uploads/3df4306e78b047aab20c9204cdbfa18b/477327bb79da4a1a9b211f8875515c2d_Group_1857.png new file mode 100644 index 0000000..30eba96 Binary files /dev/null and b/uploads/3df4306e78b047aab20c9204cdbfa18b/477327bb79da4a1a9b211f8875515c2d_Group_1857.png differ diff --git a/uploads/78d954903b734dba85006c08fc98ce50/d7dda1e32bb348a8b7392acf94cfe838_Group_1857.png b/uploads/78d954903b734dba85006c08fc98ce50/d7dda1e32bb348a8b7392acf94cfe838_Group_1857.png new file mode 100644 index 0000000..30eba96 Binary files /dev/null and b/uploads/78d954903b734dba85006c08fc98ce50/d7dda1e32bb348a8b7392acf94cfe838_Group_1857.png differ diff --git a/uploads/8513ee2ea4ac4af3b57acfaae98e4262/1ae6d1ed8b3546fb9890ac0eb89e4b0f_Group_1857.png b/uploads/8513ee2ea4ac4af3b57acfaae98e4262/1ae6d1ed8b3546fb9890ac0eb89e4b0f_Group_1857.png new file mode 100644 index 0000000..30eba96 Binary files /dev/null and b/uploads/8513ee2ea4ac4af3b57acfaae98e4262/1ae6d1ed8b3546fb9890ac0eb89e4b0f_Group_1857.png differ diff --git a/uploads/cffcea0bf5984d2ea08de3b3d4f58644/0f78e8f0473143fa836b1d49391a3869_Group_1857.png b/uploads/cffcea0bf5984d2ea08de3b3d4f58644/0f78e8f0473143fa836b1d49391a3869_Group_1857.png new file mode 100644 index 0000000..30eba96 Binary files /dev/null and b/uploads/cffcea0bf5984d2ea08de3b3d4f58644/0f78e8f0473143fa836b1d49391a3869_Group_1857.png differ