diff --git a/__pycache__/forms.cpython-313.pyc b/__pycache__/forms.cpython-313.pyc index 4f89d08..9df81d2 100644 Binary files a/__pycache__/forms.cpython-313.pyc and b/__pycache__/forms.cpython-313.pyc differ diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 88f6758..ed33182 100644 Binary files a/routes/__pycache__/main.cpython-313.pyc and b/routes/__pycache__/main.cpython-313.pyc differ diff --git a/routes/__pycache__/room_files.cpython-313.pyc b/routes/__pycache__/room_files.cpython-313.pyc index 21e9e67..6f88d64 100644 Binary files a/routes/__pycache__/room_files.cpython-313.pyc and b/routes/__pycache__/room_files.cpython-313.pyc differ diff --git a/routes/__pycache__/room_members.cpython-313.pyc b/routes/__pycache__/room_members.cpython-313.pyc index 7e6e591..84896b2 100644 Binary files a/routes/__pycache__/room_members.cpython-313.pyc and b/routes/__pycache__/room_members.cpython-313.pyc differ diff --git a/routes/__pycache__/rooms.cpython-313.pyc b/routes/__pycache__/rooms.cpython-313.pyc index 9066e79..2d9fe1d 100644 Binary files a/routes/__pycache__/rooms.cpython-313.pyc and b/routes/__pycache__/rooms.cpython-313.pyc differ diff --git a/routes/__pycache__/trash.cpython-313.pyc b/routes/__pycache__/trash.cpython-313.pyc index af2d832..56e026c 100644 Binary files a/routes/__pycache__/trash.cpython-313.pyc and b/routes/__pycache__/trash.cpython-313.pyc differ diff --git a/routes/main.py b/routes/main.py index c874956..206733b 100644 --- a/routes/main.py +++ b/routes/main.py @@ -10,6 +10,7 @@ import logging import sys import time from forms import CompanySettingsForm +from utils import log_event # Set up logging to show in console logging.basicConfig( diff --git a/routes/room_files.py b/routes/room_files.py index b0ca7d9..17b8583 100644 --- a/routes/room_files.py +++ b/routes/room_files.py @@ -29,6 +29,7 @@ import shutil import io import zipfile from datetime import datetime +from utils import log_event # Blueprint for room file operations room_files_bp = Blueprint('room_files', __name__, url_prefix='/api/rooms') @@ -221,6 +222,18 @@ def upload_room_file(room_id): existing_file.uploaded_by = current_user.id existing_file.uploaded_at = datetime.utcnow() db.session.commit() + log_event( + event_type='file_upload', + details={ + 'uploaded_by': f"{current_user.username} {current_user.last_name}", + 'filename': filename, + 'room_id': room_id, + 'path': rel_path, + 'size': stat.st_size, + 'overwritten': True + }, + user_id=current_user.id + ) return jsonify({'success': True, 'filename': filename, 'overwritten': True}) else: rf = RoomFile( @@ -235,6 +248,18 @@ def upload_room_file(room_id): ) db.session.add(rf) db.session.commit() + log_event( + event_type='file_upload', + details={ + 'uploaded_by': f"{current_user.username} {current_user.last_name}", + 'filename': filename, + 'room_id': room_id, + 'path': rel_path, + 'size': stat.st_size, + 'overwritten': False + }, + user_id=current_user.id + ) return jsonify({'success': True, 'filename': filename}) @room_files_bp.route('//files/', methods=['GET']) @@ -262,6 +287,18 @@ def download_room_file(room_id, filename): file_path = os.path.join(room_dir, rel_path, filename) if rel_path else os.path.join(room_dir, filename) if not os.path.exists(file_path): return jsonify({'error': 'File not found'}), 404 + + log_event( + event_type='file_download', + details={ + 'downloaded_by': f"{current_user.username} {current_user.last_name}", + 'filename': filename, + 'room_id': room_id, + 'path': rel_path, + 'size': rf.size if rf else None + }, + user_id=current_user.id + ) return send_from_directory(os.path.dirname(file_path), filename, as_attachment=True) @room_files_bp.route('//files/', methods=['DELETE']) @@ -294,6 +331,19 @@ def delete_file(room_id, filename): rf.deleted_at = datetime.utcnow() db.session.commit() + log_event( + event_type='file_delete', + details={ + 'deleted_by': f"{current_user.username} {current_user.last_name}", + 'filename': filename, + 'room_id': room_id, + 'path': rel_path, + 'type': rf.type, + 'size': rf.size if rf.type == 'file' else None + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @room_files_bp.route('//folders', methods=['POST']) @@ -429,6 +479,22 @@ def rename_room_file(room_id): item.path = item.path.replace(old_folder_path, new_folder_path, 1) db.session.commit() + + log_event( + event_type='file_rename', + details={ + 'renamed_by': f"{current_user.username} {current_user.last_name}", + 'old_name': old_name, + 'new_name': new_name, + 'room_id': room_id, + 'path': rel_path, + 'type': rf.type, + 'size': rf.size if rf.type == 'file' else None, + 'is_folder': os.path.isdir(new_path) + }, + user_id=current_user.id + ) + return jsonify({'success': True, 'old_name': old_name, 'new_name': new_name}) @room_files_bp.route('//download-zip', methods=['POST']) @@ -554,6 +620,20 @@ def move_room_file(room_id): rf.modified = os.path.getmtime(target_file_path) db.session.commit() + log_event( + event_type='file_move', + details={ + 'moved_by': f"{current_user.username} {current_user.last_name}", + 'filename': filename, + 'room_id': room_id, + 'source_path': source_path, + 'target_path': target_path, + 'type': rf.type, + 'size': rf.size if rf.type == 'file' else None + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @room_files_bp.route('//folders', methods=['GET']) @@ -822,6 +902,19 @@ def restore_file(room_id): rf.deleted = False db.session.commit() + log_event( + event_type='file_restore', + details={ + 'restored_by': f"{current_user.username} {current_user.last_name}", + 'filename': filename, + 'room_id': room_id, + 'path': rel_path, + 'type': rf.type, + 'size': rf.size if rf.type == 'file' else None + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @room_files_bp.route('//delete-permanent', methods=['POST']) @@ -880,6 +973,19 @@ def delete_permanent(room_id): for item in contained_items: db.session.delete(item) + + log_event( + event_type='file_delete_permanent', + details={ + 'deleted_by': f"{current_user.username} {current_user.last_name}", + 'filename': rf.name, + 'room_id': room_id, + 'path': rf.path, + 'type': rf.type, + 'size': rf.size if rf.type == 'file' else None + }, + user_id=current_user.id + ) except Exception as e: print(f"Error deleting {rf.type} from storage: {e}") diff --git a/routes/room_members.py b/routes/room_members.py index 0cf2585..91eec30 100644 --- a/routes/room_members.py +++ b/routes/room_members.py @@ -1,7 +1,7 @@ from flask import Blueprint, jsonify, request, abort from flask_login import login_required, current_user from models import db, Room, User, RoomMemberPermission -from utils import user_has_permission +from utils import user_has_permission, log_event room_members_bp = Blueprint('room_members', __name__) @@ -70,6 +70,19 @@ def add_room_member(room_id): db.session.commit() + log_event( + event_type='room_member_add', + details={ + 'room_id': room_id, + 'room_name': room.name, + 'added_user_id': user_id, + 'added_user_name': f"{user.username} {user.last_name}", + 'added_by': f"{current_user.username} {current_user.last_name}", + 'permissions': permissions + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @room_members_bp.route('//members/', methods=['DELETE']) @@ -92,6 +105,18 @@ def remove_room_member(room_id, user_id): db.session.commit() + log_event( + event_type='room_member_remove', + details={ + 'room_id': room_id, + 'room_name': room.name, + 'removed_user_id': user_id, + 'removed_user_name': f"{user.username} {user.last_name}", + 'removed_by': f"{current_user.username} {current_user.last_name}" + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @room_members_bp.route('//members//permissions', methods=['PUT']) @@ -118,4 +143,26 @@ def update_member_permissions(room_id, user_id): db.session.commit() + log_event( + event_type='room_member_permissions_update', + details={ + 'room_id': room_id, + 'room_name': room.name, + 'user_id': user_id, + 'user_name': f"{permission.user.username} {permission.user.last_name}", + 'updated_by': f"{current_user.username} {current_user.last_name}", + 'old_permissions': { + 'can_view': permission.can_view, + 'can_download': permission.can_download, + 'can_upload': permission.can_upload, + 'can_delete': permission.can_delete, + 'can_rename': permission.can_rename, + 'can_move': permission.can_move, + 'can_share': permission.can_share + }, + 'new_permissions': permissions + }, + user_id=current_user.id + ) + return jsonify({'success': True}) \ No newline at end of file diff --git a/routes/rooms.py b/routes/rooms.py index ed8f6ed..b0abf7a 100644 --- a/routes/rooms.py +++ b/routes/rooms.py @@ -4,6 +4,7 @@ from models import db, Room, User, RoomMemberPermission, RoomFile from forms import RoomForm from routes.room_files import user_has_permission from routes.auth import require_password_change +from utils import log_event rooms_bp = Blueprint('rooms', __name__, url_prefix='/rooms') @@ -53,6 +54,17 @@ def create_room(): db.session.add(creator_permission) db.session.commit() + log_event( + event_type='room_create', + details={ + 'room_id': room.id, + 'room_name': room.name, + 'description': room.description, + 'created_by': f"{current_user.username} {current_user.last_name}" + }, + user_id=current_user.id + ) + flash('Room created successfully!', 'success') return redirect(url_for('rooms.rooms')) return render_template('rooms/create_room.html', form=form) @@ -74,6 +86,17 @@ def room(room_id): can_rename = user_has_permission(room, 'can_rename') can_move = user_has_permission(room, 'can_move') can_share = user_has_permission(room, 'can_share') + + log_event( + event_type='room_open', + details={ + 'room_id': room_id, + 'room_name': room.name, + 'accessed_by': f"{current_user.username} {current_user.last_name}" + }, + user_id=current_user.id + ) + return render_template('rooms/room.html', room=room, can_download=can_download, can_upload=can_upload, can_delete=can_delete, can_rename=can_rename, can_move=can_move, can_share=can_share) @rooms_bp.route('//members') @@ -183,9 +206,26 @@ def edit_room(room_id): form = RoomForm() if form.validate_on_submit(): + old_name = room.name + old_description = room.description + room.name = form.name.data room.description = form.description.data db.session.commit() + + log_event( + event_type='room_update', + details={ + 'room_id': room.id, + 'old_name': old_name, + 'new_name': room.name, + 'old_description': old_description, + 'new_description': room.description, + 'updated_by': f"{current_user.username} {current_user.last_name}" + }, + user_id=current_user.id + ) + flash('Room updated successfully!', 'success') return redirect(url_for('rooms.rooms')) @@ -214,6 +254,16 @@ def delete_room(room_id): db.session.commit() print("Room deleted successfully") + log_event( + event_type='room_delete', + details={ + 'room_id': room_id, + 'room_name': room_name, + 'deleted_by': f"{current_user.username} {current_user.last_name}" + }, + user_id=current_user.id + ) + flash(f'Room "{room_name}" has been deleted.', 'success') except Exception as e: db.session.rollback() diff --git a/routes/trash.py b/routes/trash.py index 3148252..9acaa0b 100644 --- a/routes/trash.py +++ b/routes/trash.py @@ -1,7 +1,7 @@ from flask import Blueprint, jsonify, request, abort from flask_login import login_required, current_user from models import db, Room, RoomFile, TrashedFile, UserStarredFile -from utils import user_has_permission, clean_path +from utils import user_has_permission, clean_path, log_event import os from datetime import datetime @@ -77,6 +77,20 @@ def restore_file(room_id, trash_id): db.session.delete(trashed_file) db.session.commit() + log_event( + event_type='file_restore', + details={ + 'filename': trashed_file.name, + 'room_id': room_id, + 'room_name': room.name, + 'original_path': trashed_file.original_path, + 'restored_by': f"{current_user.username} {current_user.last_name}", + 'file_type': trashed_file.type, + 'file_size': trashed_file.size if trashed_file.type == 'file' else None + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @trash_bp.route('//trash/', methods=['DELETE']) @@ -94,6 +108,21 @@ def permanently_delete_file(room_id, trash_id): db.session.delete(trashed_file) db.session.commit() + log_event( + event_type='file_delete_permanent', + details={ + 'filename': trashed_file.name, + 'room_id': room_id, + 'room_name': room.name, + 'original_path': trashed_file.original_path, + 'deleted_by': f"{current_user.username} {current_user.last_name}", + 'file_type': trashed_file.type, + 'file_size': trashed_file.size if trashed_file.type == 'file' else None, + 'was_in_trash': True + }, + user_id=current_user.id + ) + return jsonify({'success': True}) @trash_bp.route('//trash/empty', methods=['POST']) @@ -101,6 +130,7 @@ def permanently_delete_file(room_id, trash_id): def empty_trash(room_id): """Empty the trash for a specific room.""" try: + room = Room.query.get_or_404(room_id) # Get all trashed files trashed_files = TrashedFile.query.filter_by(room_id=room_id).all() @@ -125,6 +155,17 @@ def empty_trash(room_id): db.session.commit() + log_event( + event_type='trash_empty', + details={ + 'room_id': room_id, + 'room_name': room.name, + 'emptied_by': f"{current_user.username} {current_user.last_name}", + 'files_deleted_count': len(trashed_files) + }, + user_id=current_user.id + ) + return jsonify({'success': True}) except Exception as e: db.session.rollback()