diff --git a/.env b/.env new file mode 100644 index 0000000..ab4acc4 --- /dev/null +++ b/.env @@ -0,0 +1,12 @@ +# Flask Environment +FLASK_ENV=development +UPLOAD_FOLDER=uploads + +# Database Configuration +DB_USER=root +DB_PASSWORD=hasler619 +DB_HOST=localhost +DB_PORT=3306 +DB_NAME=flask_uploads_db +SQLALCHEMY_DATABASE_URI=mysql+pymysql://root:hasler619@localhost:3306/flask_uploads_db +# SQLALCHEMY_TRACK_MODIFICATIONS=False \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a9b7921 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.analysis.extraPaths": [ + "./models" + ] +} \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..ea0a1d1 --- /dev/null +++ b/app.py @@ -0,0 +1,33 @@ +from flask import Flask +from config import Config +from extensions import db +from controllers.file_controller import file_bp +from models.uploaded_file import UploadedFile # +import logging, os + +# forda logs +log_folder = "logs" +os.makedirs(log_folder, exist_ok=True) + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler(os.path.join(log_folder, "app.log")), + logging.StreamHandler() + ] +) + +def create_app(): + app = Flask(__name__) + app.config.from_object(Config) + db.init_app(app) + app.register_blueprint(file_bp) + with app.app_context(): + db.create_all() + return app + +app = create_app() + +if __name__ == '__main__': + app.run(debug=True) diff --git a/config.py b/config.py new file mode 100644 index 0000000..be2a99d --- /dev/null +++ b/config.py @@ -0,0 +1,13 @@ +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') + DEBUG = True diff --git a/controllers/file_controller.py b/controllers/file_controller.py new file mode 100644 index 0000000..204b955 --- /dev/null +++ b/controllers/file_controller.py @@ -0,0 +1,65 @@ +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, os, logging + +file_bp = Blueprint('file_bp', __name__) + +# POST — Upload +@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 = os.path.join(current_app.config['UPLOAD_FOLDER'], uuid.uuid4().hex) + uploaded = save_file(file, folder) + return jsonify({'message': 'Upload successful','id': uploaded.id,'path': uploaded.filepath}), 201 + except Exception as e: + logging.error(f"Upload error: {e}") + return jsonify({'message': str(e)}), 400 + +# GET — Download +@file_bp.route('/file/', methods=['GET']) +def get_file(file_id): + try: + file_record = UploadedFile.query.get(file_id) + if not file_record: + return jsonify({'message': 'File not found'}), 404 + return send_file(file_record.filepath, as_attachment=True) + except Exception as e: + logging.error(f"Get error: {e}") + return jsonify({'message': str(e)}), 400 + +# PUT — Update +@file_bp.route('/file/', methods=['PUT']) +def update_uploaded_file(file_id): + try: + if 'file' not in request.files: + return jsonify({"message": "No file part"}), 400 + + file = request.files['file'] + if file.filename == '': + return jsonify({"message": "No selected file"}), 400 + + updated_record = update_file(file_id, file) + return jsonify({ + "message": "File updated successfully", + "id": updated_record.id, + "path": updated_record.filepath + }), 200 + + except Exception as e: + logging.error(str(e)) + return jsonify({"message": str(e)}), 500 + +# DELETE — Delete +@file_bp.route('/file/', methods=['DELETE']) +def delete_uploaded_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 + diff --git a/extensions.py b/extensions.py new file mode 100644 index 0000000..589c64f --- /dev/null +++ b/extensions.py @@ -0,0 +1,2 @@ +from flask_sqlalchemy import SQLAlchemy +db = SQLAlchemy() \ No newline at end of file diff --git a/logs/app.log b/logs/app.log new file mode 100644 index 0000000..8f9bfda --- /dev/null +++ b/logs/app.log @@ -0,0 +1,269 @@ +2025-10-18 14:24:29,672 [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-18 14:24:29,673 [INFO] Press CTRL+C to quit +2025-10-18 15:45:07,028 [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-18 15:45:07,029 [INFO] Press CTRL+C to quit +2025-10-18 15:45:07,031 [INFO] * Restarting with stat +2025-10-18 15:45:07,610 [WARNING] * Debugger is active! +2025-10-18 15:45:07,615 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 16:21:49,356 [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-18 16:21:49,356 [INFO] Press CTRL+C to quit +2025-10-18 16:21:49,359 [INFO] * Restarting with stat +2025-10-18 16:21:49,952 [WARNING] * Debugger is active! +2025-10-18 16:21:49,957 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 16:34:59,772 [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-18 16:34:59,772 [INFO] Press CTRL+C to quit +2025-10-18 16:34:59,774 [INFO] * Restarting with stat +2025-10-18 16:35:00,367 [WARNING] * Debugger is active! +2025-10-18 16:35:00,372 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 16:46:54,468 [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-18 16:46:54,468 [INFO] Press CTRL+C to quit +2025-10-18 16:46:54,470 [INFO] * Restarting with stat +2025-10-18 16:46:55,026 [WARNING] * Debugger is active! +2025-10-18 16:46:55,031 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 16:55:30,647 [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-18 16:55:30,647 [INFO] Press CTRL+C to quit +2025-10-18 16:55:30,648 [INFO] * Restarting with stat +2025-10-18 16:55:31,362 [WARNING] * Debugger is active! +2025-10-18 16:55:31,368 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 17:06:53,434 [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-18 17:06:53,435 [INFO] Press CTRL+C to quit +2025-10-18 17:06:53,436 [INFO] * Restarting with stat +2025-10-18 17:06:53,998 [WARNING] * Debugger is active! +2025-10-18 17:06:54,003 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 17:19:26,355 [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-18 17:19:26,356 [INFO] Press CTRL+C to quit +2025-10-18 17:19:26,357 [INFO] * Restarting with stat +2025-10-18 17:19:26,933 [WARNING] * Debugger is active! +2025-10-18 17:19:26,937 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 17:20:30,297 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:20:30,298 [INFO] 127.0.0.1 - - [18/Oct/2025 17:20:30] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:22:09,142 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:22:09,142 [INFO] 127.0.0.1 - - [18/Oct/2025 17:22:09] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:32:37,541 [INFO] 127.0.0.1 - - [18/Oct/2025 17:32:37] "GET /file/2 HTTP/1.1" 404 - +2025-10-18 17:32:46,570 [INFO] 127.0.0.1 - - [18/Oct/2025 17:32:46] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 17:33:22,194 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:33:22,195 [INFO] 127.0.0.1 - - [18/Oct/2025 17:33:22] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:33:26,019 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:33:26,019 [INFO] 127.0.0.1 - - [18/Oct/2025 17:33:26] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:33:47,989 [INFO] 127.0.0.1 - - [18/Oct/2025 17:33:47] "PUT /upload HTTP/1.1" 405 - +2025-10-18 17:33:57,342 [INFO] 127.0.0.1 - - [18/Oct/2025 17:33:57] "PUT /upload HTTP/1.1" 405 - +2025-10-18 17:35:34,617 [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-18 17:35:34,617 [INFO] Press CTRL+C to quit +2025-10-18 17:35:34,619 [INFO] * Restarting with stat +2025-10-18 17:35:35,259 [WARNING] * Debugger is active! +2025-10-18 17:35:35,264 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 17:36:01,061 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:36:01,064 [INFO] 127.0.0.1 - - [18/Oct/2025 17:36:01] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:45:32,692 [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-18 17:45:32,693 [INFO] Press CTRL+C to quit +2025-10-18 17:45:32,695 [INFO] * Restarting with stat +2025-10-18 17:45:33,299 [WARNING] * Debugger is active! +2025-10-18 17:45:33,304 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 17:46:19,371 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:46:19,374 [INFO] 127.0.0.1 - - [18/Oct/2025 17:46:19] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:50:05,857 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:50:05,858 [INFO] 127.0.0.1 - - [18/Oct/2025 17:50:05] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:50:51,515 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:50:51,517 [INFO] 127.0.0.1 - - [18/Oct/2025 17:50:51] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:56:37,085 [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-18 17:56:37,086 [INFO] Press CTRL+C to quit +2025-10-18 17:56:37,088 [INFO] * Restarting with stat +2025-10-18 17:56:37,641 [WARNING] * Debugger is active! +2025-10-18 17:56:37,646 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 17:57:15,054 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:57:15,056 [INFO] 127.0.0.1 - - [18/Oct/2025 17:57:15] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:57:26,145 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:57:26,146 [INFO] 127.0.0.1 - - [18/Oct/2025 17:57:26] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:57:27,163 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:57:27,163 [INFO] 127.0.0.1 - - [18/Oct/2025 17:57:27] "POST /upload HTTP/1.1" 400 - +2025-10-18 17:57:52,836 [INFO] 127.0.0.1 - - [18/Oct/2025 17:57:52] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 17:57:57,217 [INFO] 127.0.0.1 - - [18/Oct/2025 17:57:57] "GET /file/2 HTTP/1.1" 404 - +2025-10-18 17:58:10,503 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 17:58:10,503 [INFO] 127.0.0.1 - - [18/Oct/2025 17:58:10] "POST /upload HTTP/1.1" 400 - +2025-10-18 18:00:37,068 [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-18 18:00:37,068 [INFO] Press CTRL+C to quit +2025-10-18 18:00:37,070 [INFO] * Restarting with stat +2025-10-18 18:00:37,608 [WARNING] * Debugger is active! +2025-10-18 18:00:37,611 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 18:01:00,662 [ERROR] Upload error: 'file_path' is an invalid keyword argument for UploadedFile +2025-10-18 18:01:00,663 [INFO] 127.0.0.1 - - [18/Oct/2025 18:01:00] "POST /upload HTTP/1.1" 400 - +2025-10-18 18:01:37,306 [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-18 18:01:37,306 [INFO] Press CTRL+C to quit +2025-10-18 18:01:37,308 [INFO] * Restarting with stat +2025-10-18 18:01:37,874 [WARNING] * Debugger is active! +2025-10-18 18:01:37,878 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 18:02:04,803 [INFO] File saved: uploads\98bbff65c0304cccba0c7bc6dd0e4770\f188dc3c99ba4915aa74992fd52bee8e\Screenshot_133.png +2025-10-18 18:02:04,808 [INFO] 127.0.0.1 - - [18/Oct/2025 18:02:04] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:02:50,760 [INFO] File saved: uploads\7b354864a7544e00a160bb8452b1bd28\edc74c9925ed441db07a950af1ce623a\douma.jpg +2025-10-18 18:02:50,763 [INFO] 127.0.0.1 - - [18/Oct/2025 18:02:50] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:03:01,012 [INFO] 127.0.0.1 - - [18/Oct/2025 18:03:01] "GET /file/1 HTTP/1.1" 200 - +2025-10-18 18:03:08,494 [INFO] 127.0.0.1 - - [18/Oct/2025 18:03:08] "GET /file/2 HTTP/1.1" 200 - +2025-10-18 18:03:11,445 [INFO] 127.0.0.1 - - [18/Oct/2025 18:03:11] "GET /file/3 HTTP/1.1" 404 - +2025-10-18 18:03:25,658 [INFO] File saved: uploads\d852350d147f411ab3aa3b92ad9f9546\6107c90fc34946dbb427af01f2e66fad\doumareal.jpg +2025-10-18 18:03:25,660 [INFO] 127.0.0.1 - - [18/Oct/2025 18:03:25] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:03:45,800 [INFO] 127.0.0.1 - - [18/Oct/2025 18:03:45] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 18:04:07,070 [INFO] File deleted from storage: uploads\98bbff65c0304cccba0c7bc6dd0e4770\f188dc3c99ba4915aa74992fd52bee8e\Screenshot_133.png +2025-10-18 18:04:07,082 [INFO] Record deleted: ID=1 +2025-10-18 18:04:07,083 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:07] "DELETE /file/1 HTTP/1.1" 200 - +2025-10-18 18:04:28,901 [ERROR] Update error: update_file() takes 2 positional arguments but 3 were given +2025-10-18 18:04:28,902 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:28] "PUT /file/1 HTTP/1.1" 400 - +2025-10-18 18:04:38,114 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:38] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:04:41,681 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:41] "GET /file/2 HTTP/1.1" 200 - +2025-10-18 18:04:45,351 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:45] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 18:04:47,732 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:47] "GET /file/4 HTTP/1.1" 404 - +2025-10-18 18:04:49,780 [INFO] 127.0.0.1 - - [18/Oct/2025 18:04:49] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:05:12,989 [INFO] File saved: uploads\b6044480a0974a23aa785bff69e85ed0\aa544ecc78614a86a137fac0d01e5a74\Screenshot_99.png +2025-10-18 18:05:12,991 [INFO] 127.0.0.1 - - [18/Oct/2025 18:05:12] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:05:22,960 [INFO] 127.0.0.1 - - [18/Oct/2025 18:05:22] "GET /file/4 HTTP/1.1" 200 - +2025-10-18 18:05:37,327 [ERROR] Update error: update_file() takes 2 positional arguments but 3 were given +2025-10-18 18:05:37,327 [INFO] 127.0.0.1 - - [18/Oct/2025 18:05:37] "PUT /file/1 HTTP/1.1" 400 - +2025-10-18 18:07:43,489 [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-18 18:07:43,490 [INFO] Press CTRL+C to quit +2025-10-18 18:07:43,492 [INFO] * Restarting with stat +2025-10-18 18:07:44,090 [WARNING] * Debugger is active! +2025-10-18 18:07:44,153 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 18:08:04,215 [INFO] 127.0.0.1 - - [18/Oct/2025 18:08:04] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:08:31,610 [ERROR] File with ID 1 not found +2025-10-18 18:08:31,611 [INFO] 127.0.0.1 - - [18/Oct/2025 18:08:31] "PUT /file/1 HTTP/1.1" 500 - +2025-10-18 18:08:38,523 [ERROR] File with ID 1 not found +2025-10-18 18:08:38,524 [INFO] 127.0.0.1 - - [18/Oct/2025 18:08:38] "PUT /file/1 HTTP/1.1" 500 - +2025-10-18 18:08:43,898 [INFO] 127.0.0.1 - - [18/Oct/2025 18:08:43] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:10:20,665 [INFO] 127.0.0.1 - - [18/Oct/2025 18:10:20] "GET /file/2 HTTP/1.1" 200 - +2025-10-18 18:10:23,685 [INFO] 127.0.0.1 - - [18/Oct/2025 18:10:23] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 18:10:26,572 [INFO] 127.0.0.1 - - [18/Oct/2025 18:10:26] "GET /file/4 HTTP/1.1" 200 - +2025-10-18 18:10:30,902 [INFO] 127.0.0.1 - - [18/Oct/2025 18:10:30] "GET /file/5 HTTP/1.1" 404 - +2025-10-18 18:10:50,181 [INFO] Old file removed: uploads\b6044480a0974a23aa785bff69e85ed0\aa544ecc78614a86a137fac0d01e5a74\Screenshot_99.png +2025-10-18 18:10:50,193 [INFO] File updated: ID=4, Path=uploads\b6044480a0974a23aa785bff69e85ed0\aa544ecc78614a86a137fac0d01e5a74\Screenshot_2025-09-11_050612.png +2025-10-18 18:10:50,196 [INFO] 127.0.0.1 - - [18/Oct/2025 18:10:50] "PUT /file/4 HTTP/1.1" 200 - +2025-10-18 18:11:02,129 [INFO] 127.0.0.1 - - [18/Oct/2025 18:11:02] "GET /file/5 HTTP/1.1" 404 - +2025-10-18 18:11:04,461 [INFO] 127.0.0.1 - - [18/Oct/2025 18:11:04] "GET /file/4 HTTP/1.1" 200 - +2025-10-18 18:11:07,330 [INFO] 127.0.0.1 - - [18/Oct/2025 18:11:07] "GET /file/5 HTTP/1.1" 404 - +2025-10-18 18:12:30,804 [INFO] 127.0.0.1 - - [18/Oct/2025 18:12:30] "POST /upload/1 HTTP/1.1" 404 - +2025-10-18 18:12:55,342 [INFO] 127.0.0.1 - - [18/Oct/2025 18:12:55] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:14:30,833 [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-18 18:14:30,834 [INFO] Press CTRL+C to quit +2025-10-18 18:14:30,835 [INFO] * Restarting with stat +2025-10-18 18:14:31,427 [WARNING] * Debugger is active! +2025-10-18 18:14:31,430 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 18:14:57,754 [INFO] File saved: uploads\9b57cc3fa6dd47e59f99bb62368a5f12\a27f837c728a4898af62d53505f54371\douma.jpg +2025-10-18 18:14:57,756 [INFO] 127.0.0.1 - - [18/Oct/2025 18:14:57] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:15:08,715 [INFO] 127.0.0.1 - - [18/Oct/2025 18:15:08] "GET /file/1 HTTP/1.1" 200 - +2025-10-18 18:15:23,911 [INFO] Old file removed: uploads\9b57cc3fa6dd47e59f99bb62368a5f12\a27f837c728a4898af62d53505f54371\douma.jpg +2025-10-18 18:15:23,920 [INFO] File updated: ID=1, Path=uploads\9b57cc3fa6dd47e59f99bb62368a5f12\a27f837c728a4898af62d53505f54371\doumareal.jpg +2025-10-18 18:15:23,922 [INFO] 127.0.0.1 - - [18/Oct/2025 18:15:23] "PUT /file/1 HTTP/1.1" 200 - +2025-10-18 18:15:31,588 [INFO] 127.0.0.1 - - [18/Oct/2025 18:15:31] "GET /file/2 HTTP/1.1" 404 - +2025-10-18 18:15:34,681 [INFO] 127.0.0.1 - - [18/Oct/2025 18:15:34] "GET /file/1 HTTP/1.1" 200 - +2025-10-18 18:18:23,840 [INFO] 127.0.0.1 - - [18/Oct/2025 18:18:23] "GET /file/1 HTTP/1.1" 200 - +2025-10-18 18:18:27,707 [INFO] 127.0.0.1 - - [18/Oct/2025 18:18:27] "GET /file/2 HTTP/1.1" 404 - +2025-10-18 18:18:32,303 [INFO] 127.0.0.1 - - [18/Oct/2025 18:18:32] "GET /file/1 HTTP/1.1" 200 - +2025-10-18 18:18:36,646 [INFO] File deleted from storage: uploads\9b57cc3fa6dd47e59f99bb62368a5f12\a27f837c728a4898af62d53505f54371\doumareal.jpg +2025-10-18 18:18:36,664 [INFO] Record deleted: ID=1 +2025-10-18 18:18:36,665 [INFO] 127.0.0.1 - - [18/Oct/2025 18:18:36] "DELETE /file/1 HTTP/1.1" 200 - +2025-10-18 18:18:39,757 [INFO] 127.0.0.1 - - [18/Oct/2025 18:18:39] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:18:46,027 [ERROR] File with ID 1 not found +2025-10-18 18:18:46,028 [INFO] 127.0.0.1 - - [18/Oct/2025 18:18:46] "PUT /file/1 HTTP/1.1" 500 - +2025-10-18 18:32:05,250 [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-18 18:32:05,250 [INFO] Press CTRL+C to quit +2025-10-18 18:32:05,253 [INFO] * Restarting with stat +2025-10-18 18:32:06,291 [WARNING] * Debugger is active! +2025-10-18 18:32:06,299 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 18:32:15,246 [INFO] 127.0.0.1 - - [18/Oct/2025 18:32:15] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:32:51,447 [INFO] File saved: uploads\cc0878eef12a4aafac11910757473d96\3fa30c2947f64d47a7a51094ee8dcdbb\douma.jpg +2025-10-18 18:32:51,453 [INFO] 127.0.0.1 - - [18/Oct/2025 18:32:51] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:33:04,944 [INFO] 127.0.0.1 - - [18/Oct/2025 18:33:04] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 18:33:06,869 [INFO] 127.0.0.1 - - [18/Oct/2025 18:33:06] "GET /file/2 HTTP/1.1" 200 - +2025-10-18 18:33:34,084 [INFO] File saved: uploads\1223ad4e9f3f441ca75cbe8cad06f813\f20c4b61551e459c82a066acc73006af\Screenshot_94.png +2025-10-18 18:33:34,089 [INFO] 127.0.0.1 - - [18/Oct/2025 18:33:34] "POST /upload HTTP/1.1" 201 - +2025-10-18 18:33:43,282 [INFO] 127.0.0.1 - - [18/Oct/2025 18:33:43] "GET /filr/3 HTTP/1.1" 404 - +2025-10-18 18:33:48,271 [INFO] 127.0.0.1 - - [18/Oct/2025 18:33:48] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 18:34:13,830 [INFO] Old file removed: uploads\1223ad4e9f3f441ca75cbe8cad06f813\f20c4b61551e459c82a066acc73006af\Screenshot_94.png +2025-10-18 18:34:13,845 [INFO] File updated: ID=3, Path=uploads\1223ad4e9f3f441ca75cbe8cad06f813\f20c4b61551e459c82a066acc73006af\doumareal.jpg +2025-10-18 18:34:13,848 [INFO] 127.0.0.1 - - [18/Oct/2025 18:34:13] "PUT /file/3 HTTP/1.1" 200 - +2025-10-18 18:34:22,520 [INFO] 127.0.0.1 - - [18/Oct/2025 18:34:22] "GET /file/4 HTTP/1.1" 404 - +2025-10-18 18:34:25,497 [INFO] 127.0.0.1 - - [18/Oct/2025 18:34:25] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 18:59:03,122 [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-18 18:59:03,123 [INFO] Press CTRL+C to quit +2025-10-18 18:59:03,126 [INFO] * Restarting with stat +2025-10-18 18:59:04,281 [WARNING] * Debugger is active! +2025-10-18 18:59:04,287 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 19:00:14,931 [INFO] File saved: uploads\1c1bb353791b41aab80dc4705ea2bb84\657218bdabb24c139537f6dacb2358ff_douma.jpg +2025-10-18 19:00:14,939 [INFO] 127.0.0.1 - - [18/Oct/2025 19:00:14] "POST /upload HTTP/1.1" 201 - +2025-10-18 19:00:53,353 [INFO] File saved: uploads\e08c6d08938b46d4a179bc01ba29ea3b\7c311abcb44c4464bd9ddf96439ba291_Screenshot_63.png +2025-10-18 19:00:53,358 [INFO] 127.0.0.1 - - [18/Oct/2025 19:00:53] "POST /upload HTTP/1.1" 201 - +2025-10-18 19:01:08,513 [INFO] File saved: uploads\f0a34d5761fa4e31a345e946f21b62f7\030c4aae63224d4090f0aed0ea368420_Screenshot_142.png +2025-10-18 19:01:08,516 [INFO] 127.0.0.1 - - [18/Oct/2025 19:01:08] "POST /upload HTTP/1.1" 201 - +2025-10-18 19:01:19,826 [INFO] 127.0.0.1 - - [18/Oct/2025 19:01:19] "GET /file/4 HTTP/1.1" 404 - +2025-10-18 19:01:21,814 [INFO] 127.0.0.1 - - [18/Oct/2025 19:01:21] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 19:01:26,262 [INFO] File deleted from storage: uploads\f0a34d5761fa4e31a345e946f21b62f7\030c4aae63224d4090f0aed0ea368420_Screenshot_142.png +2025-10-18 19:01:26,275 [INFO] Record deleted: ID=3 +2025-10-18 19:01:26,276 [INFO] 127.0.0.1 - - [18/Oct/2025 19:01:26] "DELETE /file/3 HTTP/1.1" 200 - +2025-10-18 19:01:30,835 [INFO] 127.0.0.1 - - [18/Oct/2025 19:01:30] "GET /file/3 HTTP/1.1" 404 - +2025-10-18 19:01:34,737 [INFO] 127.0.0.1 - - [18/Oct/2025 19:01:34] "GET /file/2 HTTP/1.1" 200 - +2025-10-18 19:02:00,081 [INFO] File saved: uploads\ce2d12fefc57442abdcd5dc45dcb4132\0d2404c154664934bcf9995b88a395b0_doumareal.jpg +2025-10-18 19:02:00,084 [INFO] 127.0.0.1 - - [18/Oct/2025 19:02:00] "POST /upload HTTP/1.1" 201 - +2025-10-18 19:02:12,636 [INFO] 127.0.0.1 - - [18/Oct/2025 19:02:12] "GET /file/3 HTTP/1.1" 404 - +2025-10-18 19:02:15,175 [INFO] 127.0.0.1 - - [18/Oct/2025 19:02:15] "GET /file/4 HTTP/1.1" 200 - +2025-10-18 19:02:45,627 [INFO] Old file removed: uploads\e08c6d08938b46d4a179bc01ba29ea3b\7c311abcb44c4464bd9ddf96439ba291_Screenshot_63.png +2025-10-18 19:02:45,640 [INFO] File updated: ID=2, Path=uploads\e08c6d08938b46d4a179bc01ba29ea3b\3759086-3840x2160-desktop-hd-msi-logo-wallpaper-image.jpg +2025-10-18 19:02:45,643 [INFO] 127.0.0.1 - - [18/Oct/2025 19:02:45] "PUT /file/2 HTTP/1.1" 200 - +2025-10-18 19:02:54,850 [INFO] 127.0.0.1 - - [18/Oct/2025 19:02:54] "GET /file/1 HTTP/1.1" 200 - +2025-10-18 19:02:58,695 [INFO] 127.0.0.1 - - [18/Oct/2025 19:02:58] "GET /file/2 HTTP/1.1" 200 - +2025-10-18 19:03:05,256 [INFO] 127.0.0.1 - - [18/Oct/2025 19:03:05] "GET /file/3 HTTP/1.1" 404 - +2025-10-18 19:03:08,661 [INFO] 127.0.0.1 - - [18/Oct/2025 19:03:08] "GET /file/4 HTTP/1.1" 200 - +2025-10-18 20:18:24,449 [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-18 20:18:24,450 [INFO] Press CTRL+C to quit +2025-10-18 20:18:24,452 [INFO] * Restarting with stat +2025-10-18 20:18:25,543 [WARNING] * Debugger is active! +2025-10-18 20:18:25,589 [INFO] * Debugger PIN: 169-552-464 +2025-10-18 20:18:36,581 [INFO] 127.0.0.1 - - [18/Oct/2025 20:18:36] "GET /file/1 HTTP/1.1" 404 - +2025-10-18 20:19:20,080 [INFO] File saved: uploads\22d05b592e5e4e5f890c607d41a0dd56\0b217643e31949329d6b9cd37b203914_douma.jpg +2025-10-18 20:19:20,084 [INFO] 127.0.0.1 - - [18/Oct/2025 20:19:20] "POST /upload HTTP/1.1" 201 - +2025-10-18 20:19:24,059 [INFO] File saved: uploads\b49693c3f28e440abceeadb07493143c\6cb4c12c016d4a4b8ffb3434d687436a_doumareal.jpg +2025-10-18 20:19:24,061 [INFO] 127.0.0.1 - - [18/Oct/2025 20:19:24] "POST /upload HTTP/1.1" 201 - +2025-10-18 20:19:44,457 [INFO] File saved: uploads\6d443cfb344b46e5bb5532aeec1c11f9\53e3355bba5a46f8863e5a81d8340c0a_Screenshot_2025-09-26_114815.png +2025-10-18 20:19:44,461 [INFO] 127.0.0.1 - - [18/Oct/2025 20:19:44] "POST /upload HTTP/1.1" 201 - +2025-10-18 20:19:54,852 [INFO] 127.0.0.1 - - [18/Oct/2025 20:19:54] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 20:20:17,529 [INFO] Old file removed: uploads\6d443cfb344b46e5bb5532aeec1c11f9\53e3355bba5a46f8863e5a81d8340c0a_Screenshot_2025-09-26_114815.png +2025-10-18 20:20:17,546 [INFO] File updated: ID=3, Path=uploads\6d443cfb344b46e5bb5532aeec1c11f9\Screenshot_63.png +2025-10-18 20:20:17,551 [INFO] 127.0.0.1 - - [18/Oct/2025 20:20:17] "PUT /file/3 HTTP/1.1" 200 - +2025-10-18 20:20:22,809 [INFO] 127.0.0.1 - - [18/Oct/2025 20:20:22] "GET /file/4 HTTP/1.1" 404 - +2025-10-18 20:20:24,633 [INFO] 127.0.0.1 - - [18/Oct/2025 20:20:24] "GET /file/3 HTTP/1.1" 200 - +2025-10-18 20:20:45,556 [INFO] File deleted from storage: uploads\6d443cfb344b46e5bb5532aeec1c11f9\Screenshot_63.png +2025-10-18 20:20:45,557 [INFO] Empty folder removed: uploads\6d443cfb344b46e5bb5532aeec1c11f9 +2025-10-18 20:20:45,652 [INFO] Database record deleted: ID=3 +2025-10-18 20:20:45,653 [INFO] 127.0.0.1 - - [18/Oct/2025 20:20:45] "DELETE /file/3 HTTP/1.1" 200 - +2025-10-18 20:21:01,647 [INFO] File deleted from storage: uploads\b49693c3f28e440abceeadb07493143c\6cb4c12c016d4a4b8ffb3434d687436a_doumareal.jpg +2025-10-18 20:21:01,648 [INFO] Empty folder removed: uploads\b49693c3f28e440abceeadb07493143c +2025-10-18 20:21:01,737 [INFO] Database record deleted: ID=2 +2025-10-18 20:21:01,738 [INFO] 127.0.0.1 - - [18/Oct/2025 20:21:01] "DELETE /file/2 HTTP/1.1" 200 - +2025-10-18 20:21:03,885 [INFO] File deleted from storage: uploads\22d05b592e5e4e5f890c607d41a0dd56\0b217643e31949329d6b9cd37b203914_douma.jpg +2025-10-18 20:21:03,886 [INFO] Empty folder removed: uploads\22d05b592e5e4e5f890c607d41a0dd56 +2025-10-18 20:21:03,934 [INFO] Database record deleted: ID=1 +2025-10-18 20:21:03,934 [INFO] 127.0.0.1 - - [18/Oct/2025 20:21:03] "DELETE /file/1 HTTP/1.1" 200 - +2025-10-18 20:21:20,747 [INFO] File saved: uploads\f6ac8b6b4a1f4fbc960597b8aa6dd21d\d3ccd0028e314275845b043cb02cb333_douma.jpg +2025-10-18 20:21:20,749 [INFO] 127.0.0.1 - - [18/Oct/2025 20:21:20] "POST /upload HTTP/1.1" 201 - +2025-10-18 20:21:45,702 [INFO] Old file removed: uploads\f6ac8b6b4a1f4fbc960597b8aa6dd21d\d3ccd0028e314275845b043cb02cb333_douma.jpg +2025-10-18 20:21:45,773 [INFO] File updated: ID=4, Path=uploads\f6ac8b6b4a1f4fbc960597b8aa6dd21d\doumareal.jpg +2025-10-18 20:21:45,780 [INFO] 127.0.0.1 - - [18/Oct/2025 20:21:45] "PUT /file/4 HTTP/1.1" 200 - +2025-10-18 20:22:03,770 [INFO] 127.0.0.1 - - [18/Oct/2025 20:22:03] "GET /file/4 HTTP/1.1" 200 - +2025-10-18 20:22:17,679 [INFO] 127.0.0.1 - - [18/Oct/2025 20:22:17] "POST /uplaod HTTP/1.1" 404 - +2025-10-18 20:22:25,123 [INFO] File saved: uploads\f474b31b45034ec6a79f078d84045a72\8dbc86657e1645cfa1b81c5c969eb479_douma.jpg +2025-10-18 20:22:25,125 [INFO] 127.0.0.1 - - [18/Oct/2025 20:22:25] "POST /upload HTTP/1.1" 201 - diff --git a/models/uploaded_file.py b/models/uploaded_file.py new file mode 100644 index 0000000..50c481b --- /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) + filepath = db.Column(db.String(512), nullable=False) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a2fbb49 Binary files /dev/null and b/requirements.txt differ diff --git a/services/file_service.py b/services/file_service.py new file mode 100644 index 0000000..a0a7ffb --- /dev/null +++ b/services/file_service.py @@ -0,0 +1,82 @@ +import os +import uuid +import logging +from werkzeug.utils import secure_filename +from extensions import db +from models.uploaded_file import UploadedFile + +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=filename, filepath=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, file): + record = UploadedFile.query.get(file_id) + if not record: + raise ValueError(f"File with ID {file_id} not found") + + if not allowed_file(file.filename): + raise ValueError("File type not allowed") + + # delete old file if it exists + if os.path.exists(record.filepath): + os.remove(record.filepath) + logging.info(f"Old file removed: {record.filepath}") + + # save the new file in the same folder + folder = os.path.dirname(record.filepath) + filename = secure_filename(file.filename) + save_path = os.path.join(folder, filename) + file.save(save_path) + + # update database record + record.filename = filename + record.filepath = save_path + db.session.commit() + + logging.info(f"File updated: ID={file_id}, Path={save_path}") + return record + +from extensions import db +from models.uploaded_file import UploadedFile +import os, logging + +def delete_file(file_id): + record = UploadedFile.query.get(file_id) + if not record: + raise ValueError(f"File with ID {file_id} not found") + + if record.filepath and os.path.exists(record.filepath): + os.remove(record.filepath) + logging.info(f"File deleted from storage: {record.filepath}") + + folder = os.path.dirname(record.filepath) + if os.path.exists(folder) and not os.listdir(folder): + os.rmdir(folder) + logging.info(f"Empty folder removed: {folder}") + + else: + logging.warning(f"File path not found or already deleted: {record.filepath}") + + db.session.delete(record) + db.session.commit() + logging.info(f"Database record deleted: ID={file_id}") + + return True diff --git a/uploads/f474b31b45034ec6a79f078d84045a72/8dbc86657e1645cfa1b81c5c969eb479_douma.jpg b/uploads/f474b31b45034ec6a79f078d84045a72/8dbc86657e1645cfa1b81c5c969eb479_douma.jpg new file mode 100644 index 0000000..d3a34ec Binary files /dev/null and b/uploads/f474b31b45034ec6a79f078d84045a72/8dbc86657e1645cfa1b81c5c969eb479_douma.jpg differ diff --git a/uploads/f6ac8b6b4a1f4fbc960597b8aa6dd21d/doumareal.jpg b/uploads/f6ac8b6b4a1f4fbc960597b8aa6dd21d/doumareal.jpg new file mode 100644 index 0000000..66662f0 Binary files /dev/null and b/uploads/f6ac8b6b4a1f4fbc960597b8aa6dd21d/doumareal.jpg differ