Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
venv/
Binary file not shown.
Binary file added __pycache__/app.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/config.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/database.cpython-313.pyc
Binary file not shown.
36 changes: 36 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
import logging
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config
from database import db

# Initialize Flask app
app = Flask(__name__)
app.config.from_object(Config)

# Initialize database
# 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'
)

# Register blueprints
from controllers.file_controller import file_bp
from controllers.auth_controller import auth_bp
app.register_blueprint(file_bp)
app.register_blueprint(auth_bp)


db.init_app(app)

# Create tables if not exist
with app.app_context():
db.create_all()

if __name__ == '__main__':
app.run(debug=True)
10 changes: 10 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os
from dotenv import load_dotenv
load_dotenv() # Load .env variables
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
SECRET_KEY = os.environ.get('SECRET_KEY')
Binary file not shown.
Binary file not shown.
98 changes: 98 additions & 0 deletions controllers/auth_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@

from flask import Blueprint, request, jsonify, current_app
import jwt
import datetime


MOCK_USERS = [
{
"id": 1,
"username": "admin",
"password": "password123",
"name": "Administrator",
"role": "admin"
},
{
"id": 2,
"username": "user",
"password": "password",
"name": "Regular User",
"role": "user"
}
]

auth_bp = Blueprint('auth_bp', __name__)

def _find_user_by_username(username):
"""A helper function to find a user in our mock DB."""
for user in MOCK_USERS:
if user['username'] == username:
return user
return None

@auth_bp.route('/login', methods=['POST'])
def login():
"""
Handles the login request.
Validates credentials and returns user details and a JWT on success.
"""
try:
data = request.json
if not data:
return jsonify({"message": "No input data provided"}), 400

username = data.get('username')
password = data.get('password')

if not username or not password:
return jsonify({"message": "Missing username or password"}), 400


user = _find_user_by_username(username)

if not user:
current_app.logger.warning(f"Failed login attempt. User not found: {username}")
return jsonify({"message": "Username not found"}), 401


if user['password'] != password:

current_app.logger.warning(f"Wrong password for user: {username}")
return jsonify({"message": "Wrong password"}), 401


payload = {
'sub': user['id'],
'name': user['name'],
'role': user['role'],
'iat': datetime.datetime.now(),
'exp': datetime.datetime.now() + datetime.timedelta(hours=1)
}


secret_key = current_app.config['SECRET_KEY']

# token
token = jwt.encode(payload, secret_key, algorithm="HS256")


user_details = {
"id": user['id'],
"username": user['username'],
"name": user['name'],
"role": user['role']
}

current_app.logger.info(f"Login successful for user: {username}")


return jsonify({
"message": "Login successful",
"token": token,
"user": user_details
}), 200

except Exception as e:

current_app.logger.error(f"Error during login for user {username}: {e}")
return jsonify({"message": "An internal error occurred"}), 500
60 changes: 60 additions & 0 deletions controllers/file_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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 logging
import uuid
import os

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 = os.path.join(current_app.config['UPLOAD_FOLDER'], uuid.uuid4().hex) # new folder per request
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/<int:file_id>', 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/<id>
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/<int:file_id>', 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/<int:file_id>', 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
3 changes: 3 additions & 0 deletions database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
1 change: 1 addition & 0 deletions file-upload
Submodule file-upload added at ea0fb5
Loading