From 6adba904ebd124caf48b259dd7cda8affad47f94 Mon Sep 17 00:00:00 2001 From: roginand Date: Sun, 19 Oct 2025 20:46:29 +0800 Subject: [PATCH] feat(file/upload): implemented file upload --- .gitignore | 2 + __pycache__/app.cpython-313.pyc | Bin 0 -> 1499 bytes __pycache__/config.cpython-313.pyc | Bin 0 -> 1227 bytes __pycache__/extensions.cpython-313.pyc | Bin 0 -> 237 bytes app.py | 35 +++++ config.py | 25 +++ .../file_controller.cpython-313.pyc | Bin 0 -> 4047 bytes controllers/file_controller.py | 70 +++++++++ extensions.py | 5 + logs/app.log | 148 ++++++++++++++++++ .../__pycache__/uploaded_file.cpython-313.pyc | Bin 0 -> 816 bytes models/uploaded_file.py | 6 + requirements.txt | 12 ++ .../__pycache__/file_service.cpython-313.pyc | Bin 0 -> 3556 bytes services/file_service.py | 49 ++++++ ...5a5493e4ea0808db5cd9efdba5d_Group_1857.png | Bin 0 -> 1611 bytes ...64dfdddbf48e7b5634925b0df4811_24SDFDSF.png | Bin 0 -> 23080 bytes ...d81cd3c4822a09fa5cd969f44f7_Group_1857.png | Bin 0 -> 1611 bytes ...7bb79da4a1a9b211f8875515c2d_Group_1857.png | Bin 0 -> 1611 bytes ...1e32bb348a8b7392acf94cfe838_Group_1857.png | Bin 0 -> 1611 bytes ...1ed8b3546fb9890ac0eb89e4b0f_Group_1857.png | Bin 0 -> 1611 bytes ...8f0473143fa836b1d49391a3869_Group_1857.png | Bin 0 -> 1611 bytes 22 files changed, 352 insertions(+) create mode 100644 .gitignore create mode 100644 __pycache__/app.cpython-313.pyc create mode 100644 __pycache__/config.cpython-313.pyc create mode 100644 __pycache__/extensions.cpython-313.pyc create mode 100644 app.py create mode 100644 config.py create mode 100644 controllers/__pycache__/file_controller.cpython-313.pyc create mode 100644 controllers/file_controller.py create mode 100644 extensions.py create mode 100644 logs/app.log create mode 100644 models/__pycache__/uploaded_file.cpython-313.pyc create mode 100644 models/uploaded_file.py create mode 100644 requirements.txt create mode 100644 services/__pycache__/file_service.cpython-313.pyc create mode 100644 services/file_service.py create mode 100644 uploads/04840ee4ba9b48bb8d4a8801ab646c25/9b15f5a5493e4ea0808db5cd9efdba5d_Group_1857.png create mode 100644 uploads/16da22714b6e48c7855fb6f2e3035742/a1c64dfdddbf48e7b5634925b0df4811_24SDFDSF.png create mode 100644 uploads/39ef690cba1d40149c5f7f6d02479fc1/13fe5d81cd3c4822a09fa5cd969f44f7_Group_1857.png create mode 100644 uploads/3df4306e78b047aab20c9204cdbfa18b/477327bb79da4a1a9b211f8875515c2d_Group_1857.png create mode 100644 uploads/78d954903b734dba85006c08fc98ce50/d7dda1e32bb348a8b7392acf94cfe838_Group_1857.png create mode 100644 uploads/8513ee2ea4ac4af3b57acfaae98e4262/1ae6d1ed8b3546fb9890ac0eb89e4b0f_Group_1857.png create mode 100644 uploads/cffcea0bf5984d2ea08de3b3d4f58644/0f78e8f0473143fa836b1d49391a3869_Group_1857.png 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 0000000000000000000000000000000000000000..bd442696027d707daf845e971e714b6cee88c8e4 GIT binary patch literal 1499 zcmaJ>Pj4Gl5P$oBz44!>HMQ!1yts+5rL+|yDNWUKafyXpCCJ-Im29+H+v{b?de?fp zMj_&maw(-qX(do|-~+&o1K*$@fTjdByt*eYaEqE&l{hhP?bRBDc(UI7W`6T#-kX{A zaVn)CSU-o1>jqxA&sdcJeCY*KxgZ0jl-NuZCstN30Md*Ul(f< zmH-QRgymL36EELH$jBkol8b105*RBjr2)2oFHO?q6-GrCV=?8#pOjuJGelT*41W`V zSn=GRZ2@At?auUR5fpd^I{t&9S8l>SUQISa%M+W6Hc%saVnl*$NK?uLHs#0`omNpZ zEF85rk`WurB1E6^O-D@QM1*2VegEqw8Y6&{#%kOO3;9k)CmPuZ#q$4Sq>;wI!%jsw z9iNPlCTQXcZ6y@Ho~)%@My2<9BjRJZ!Ne~Ek3!5uC~7c^{!u42;$s;PLpV4vNXwH> z(d_f+O|*zg6ZbB3t_PJUK-4jNl`EhgjK~^MuCG`1)%EMw-f!H1xsc5*+YfYab6dV( z3N&)R-#p=9`-!0wx6Uu`aw9P?AtaSf_|q%M^DrtcdTC}obwJ=-xUMTF3ch&>PY zjR4cTL3oMsdV5fw#dO+;tn1WU*NNB(N0ZF-u-DhU_9td1z;wzm?In?I*A8^J5n(-_ z$bDne?Aq8T5?s)-U5m`L4d3pJ=bP{!tgWt-IS3hG&v8ubpAQqq224kfoW!OD&o!~$ zc82Bv+ipN+Ac;;L&8>ha9c&tbsT+<{W(fyWc+jWMQB|^3x!l_DA$+T9yN26sUGqA_ zzUc;j>qG2Wj^Vaw-gCo&;~Cu+E$HC0I4DGW9Rkts#{UVn9W+uh-^h0_>{JeU>2~c_ z?FpaX=kwoPJm8BjL?lSJ>tEFGaR+>MN_(K(SN5*#pD6vf^q9Xi;%A=lxyO9&Fq?Z& zyI)de;GnbxAydb_zBVh?LjkD~%d%&MP5|NVHE!-*m zn#hl2WjFOz>g&ua4k>fMBr`j;R}$cZM1G?F<vW!k zuGt=1Kku;1M1gXfu5Wv;A9g&f3{Fu#Pfu}p=>C9?0U|s{ z4f2(~*Bv_Mdw3pHm~iA@h3$xA80INTJVoheDEADVf!}jJ$7GI{5TE>V`xoxaZ%X#= Y;<&`sTh)8>J5?yxk%0I-J_`~422DdVO8@`> literal 0 HcmV?d00001 diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2a8cbb581ec27bc6e831e038736b039d97cce93 GIT binary patch literal 1227 zcmZ`(O>EOh6rM?(pC%5ar3KnTDG>{@_$e(Enl3`?CTVe#G}%l7s$^*tS~mnab~|=J z;ZzC4feQ#})oL$Pd)ab96&DZ}IC2c~0W`auIB^TxmG-)0JAqs>lHc$5=FPnK#(R-S z2(Y#J_rJGpdja@jEA7R#guNLi{00y}Fa?OsJt?^0A)YZHd!k^f1sem0K@=eGFhJat z$2Agf84A9ywz4Qd8dmkv3SQAoMZ2@y3Ue6WQuZ`xr;-7I2oMi~#EU#Kmv;`?833GA zQw(jdeJo{Hl&=Hj?}`d^pt|oB>_GK&-7EA1bsr+!5U8!$ z6JXF#Iy>Jsa;jc7Xm~Cyq7+UfXN&WcE0CHZcqwNxPT>Ug))nJ98=(jdVyu;_3dS^q zaaCWbskY2xyk0A*El)4Ts}*9HYDH5tow+@_LH%XL9y@vn{hAij@oUM<5>AK(aaKf0 zT+B;!w8K%zi}4iBWE0Yy6c-Cpb`g`70oo{9MQ{aNt0JAs&dvXj=bzNm~rZK(P^unGTYaH5Tl|@i z{Nx9I(&8^Rrqdtz^!+3`(Y}Ddk)uqee)sJY_N#Gk=9J0B{bU1zSKOXnC8wBbKW}4{ f5q3fd{{lUq!RR+Xfc%5Ve_TJzz<+>Eo%sI&hCvgX literal 0 HcmV?d00001 diff --git a/__pycache__/extensions.cpython-313.pyc b/__pycache__/extensions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d53a8b6a283974e41f1e8dcb119ec72fa997ee0 GIT binary patch literal 237 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1ad7dfc07d55d149c95b7fbb931abb08cd4f5d41 GIT binary patch literal 816 zcmbtS&ubGw6rS1L{MaNVv7k0Rh!i~R!6IrP4dTIQw2DPK_O>u=veR^NG8<=i!Q2Xh zhhA!h($XHiwGk433`krzKZ710wTsdBBqO1t!c#+)XWr0vuin-k#-!c48Pr(rT~}@ z_z2D}a*@OYNrPZ%y4LE1*zf5GhC;mbXH0v-JdD0Uqw=81TWl|Y|ud*bT zf#|CIIuOz2%GQYXy?8@iq%?}XIOtF>hO~APvk1Cr@yBqHdeCgRYY~%?`!Eom@ZI}i zC+RT}NA5EjcDW~9-R*iZ+13TRj7M(jF&{{Bw%8v?{jDcP^Nd@?orKDBRb-W!%LY!9{u`?E*YgX;0E!z(A(PRsSpN5jhOp8d|=UpR6Ooa5%<;z{ka zvh;dcUi#H|#j#X@y^5KLf>1=MAepC{PdZ)il%`(>r1E!CY46H;aP@x>&4Kx5VvN6_ S>X~KY22iW-)W0Kulggh{4!URn literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0f49435fc5c88477ae1b8a593d4aace160fec72e GIT binary patch literal 3556 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 literal 0 HcmV?d00001 diff --git a/uploads/39ef690cba1d40149c5f7f6d02479fc1/13fe5d81cd3c4822a09fa5cd969f44f7_Group_1857.png b/uploads/39ef690cba1d40149c5f7f6d02479fc1/13fe5d81cd3c4822a09fa5cd969f44f7_Group_1857.png new file mode 100644 index 0000000000000000000000000000000000000000..30eba969edbe16508e75d3b71fd190a3c09c3387 GIT binary patch literal 1611 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