from flask import render_template, Blueprint, redirect, url_for, request, flash from flask_login import current_user, login_required from models import User, db, Room, RoomFile, RoomMemberPermission import os from werkzeug.utils import secure_filename from sqlalchemy import func, case, literal_column, text from datetime import datetime, timedelta def init_routes(main_bp): @main_bp.route('/') def home(): if current_user.is_authenticated: return redirect(url_for('main.dashboard')) return render_template('home.html') @main_bp.route('/dashboard') @login_required def dashboard(): # Get 3 most recent users recent_contacts = User.query.order_by(User.created_at.desc()).limit(3).all() # Count active and inactive users active_count = User.query.filter_by(is_active=True).count() inactive_count = User.query.filter_by(is_active=False).count() # Room count and size logic if current_user.is_admin: room_count = Room.query.count() # Get total file and folder counts for admin file_count = RoomFile.query.filter_by(type='file').count() folder_count = RoomFile.query.filter_by(type='folder').count() # Get total size of all files including trash total_size = db.session.query(func.sum(RoomFile.size)).filter(RoomFile.type == 'file').scalar() or 0 # Get recent activity for all files recent_activity = db.session.query( RoomFile, Room, User ).join( Room, RoomFile.room_id == Room.id ).join( User, RoomFile.uploaded_by == User.id ).order_by( RoomFile.uploaded_at.desc() ).limit(10).all() # Format the activity data formatted_activity = [] for file, room, user in recent_activity: activity = { 'name': file.name, 'type': file.type, 'room': room, 'uploader': user, 'uploaded_at': file.uploaded_at, 'is_starred': current_user in file.starred_by, 'is_deleted': file.deleted, 'can_download': True # Admin can download everything } formatted_activity.append(activity) recent_activity = formatted_activity # Get storage usage by file type including trash storage_by_type = db.session.query( case( (RoomFile.name.like('%.%'), func.split_part(RoomFile.name, '.', -1)), else_=literal_column("'unknown'") ).label('extension'), func.count(RoomFile.id).label('count'), func.sum(RoomFile.size).label('total_size') ).filter( RoomFile.type == 'file' ).group_by('extension').all() # Get trash and starred stats for admin trash_count = RoomFile.query.filter_by(deleted=True).count() starred_count = RoomFile.query.filter(RoomFile.starred_by.contains(current_user)).count() # Get oldest trash date and total trash size oldest_trash = RoomFile.query.filter_by(deleted=True).order_by(RoomFile.deleted_at.asc()).first() oldest_trash_date = oldest_trash.deleted_at.strftime('%Y-%m-%d') if oldest_trash else None trash_size = db.session.query(db.func.sum(RoomFile.size)).filter(RoomFile.deleted==True).scalar() or 0 # Get files that will be deleted in next 7 days seven_days_from_now = datetime.utcnow() + timedelta(days=7) thirty_days_ago = datetime.utcnow() - timedelta(days=30) pending_deletion = RoomFile.query.filter( RoomFile.deleted==True, RoomFile.deleted_at <= thirty_days_ago, RoomFile.deleted_at > thirty_days_ago - timedelta(days=7) ).count() # Get trash file type breakdown trash_by_type = db.session.query( case( (RoomFile.name.like('%.%'), func.split_part(RoomFile.name, '.', -1)), else_=literal_column("'unknown'") ).label('extension'), func.count(RoomFile.id).label('count') ).filter( RoomFile.deleted==True ).group_by('extension').all() else: # Get rooms the user has access to accessible_rooms = Room.query.filter(Room.members.any(id=current_user.id)).all() room_count = len(accessible_rooms) # Get file and folder counts for accessible rooms room_ids = [room.id for room in accessible_rooms] file_count = RoomFile.query.filter(RoomFile.room_id.in_(room_ids), RoomFile.type == 'file').count() folder_count = RoomFile.query.filter(RoomFile.room_id.in_(room_ids), RoomFile.type == 'folder').count() # Get total size of files in accessible rooms including trash total_size = db.session.query(func.sum(RoomFile.size)).filter( RoomFile.room_id.in_(room_ids), RoomFile.type == 'file' ).scalar() or 0 # Get recent activity for accessible rooms recent_activity = db.session.query( RoomFile, Room, User ).join( Room, RoomFile.room_id == Room.id ).join( User, RoomFile.uploaded_by == User.id ).filter( RoomFile.room_id.in_(room_ids) ).order_by( RoomFile.uploaded_at.desc() ).limit(10).all() # Format the activity data formatted_activity = [] user_perms = {p.room_id: p for p in RoomMemberPermission.query.filter( RoomMemberPermission.room_id.in_(room_ids), RoomMemberPermission.user_id==current_user.id ).all()} for file, room, user in recent_activity: perm = user_perms.get(room.id) activity = { 'name': file.name, 'type': file.type, 'room': room, 'uploader': user, 'uploaded_at': file.uploaded_at, 'is_starred': current_user in file.starred_by, 'is_deleted': file.deleted, 'can_download': perm.can_download if perm else False } formatted_activity.append(activity) recent_activity = formatted_activity # Get storage usage by file type for accessible rooms including trash storage_by_type = db.session.query( case( (RoomFile.name.like('%.%'), func.split_part(RoomFile.name, '.', -1)), else_=literal_column("'unknown'") ).label('extension'), func.count(RoomFile.id).label('count'), func.sum(RoomFile.size).label('total_size') ).filter( RoomFile.room_id.in_(room_ids), RoomFile.type == 'file' ).group_by('extension').all() # Get trash and starred stats for user's accessible rooms trash_count = RoomFile.query.filter(RoomFile.room_id.in_(room_ids), RoomFile.deleted==True).count() starred_count = RoomFile.query.filter( RoomFile.room_id.in_(room_ids), RoomFile.starred_by.contains(current_user) ).count() # Get oldest trash date and total trash size for user's rooms oldest_trash = RoomFile.query.filter(RoomFile.room_id.in_(room_ids), RoomFile.deleted==True).order_by(RoomFile.deleted_at.asc()).first() oldest_trash_date = oldest_trash.deleted_at.strftime('%Y-%m-%d') if oldest_trash else None trash_size = db.session.query(db.func.sum(RoomFile.size)).filter(RoomFile.room_id.in_(room_ids), RoomFile.deleted==True).scalar() or 0 # Get files that will be deleted in next 7 days for user's rooms seven_days_from_now = datetime.utcnow() + timedelta(days=7) thirty_days_ago = datetime.utcnow() - timedelta(days=30) pending_deletion = RoomFile.query.filter( RoomFile.room_id.in_(room_ids), RoomFile.deleted==True, RoomFile.deleted_at <= thirty_days_ago, RoomFile.deleted_at > thirty_days_ago - timedelta(days=7) ).count() # Get trash file type breakdown for user's rooms trash_by_type = db.session.query( case( (RoomFile.name.like('%.%'), func.split_part(RoomFile.name, '.', -1)), else_=literal_column("'unknown'") ).label('extension'), func.count(RoomFile.id).label('count') ).filter( RoomFile.room_id.in_(room_ids), RoomFile.deleted==True ).group_by('extension').all() return render_template('dashboard.html', recent_contacts=recent_contacts, active_count=active_count, inactive_count=inactive_count, room_count=room_count, file_count=file_count, folder_count=folder_count, total_size=total_size, recent_activity=recent_activity, storage_by_type=storage_by_type, trash_count=trash_count, starred_count=starred_count, oldest_trash_date=oldest_trash_date, trash_size=trash_size, pending_deletion=pending_deletion, trash_by_type=trash_by_type) UPLOAD_FOLDER = os.path.join(os.getcwd(), 'uploads', 'profile_pics') if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) @main_bp.route('/profile', methods=['GET', 'POST']) @login_required def profile(): if request.method == 'POST': # Handle profile picture removal if 'remove_picture' in request.form: if current_user.profile_picture: # Delete the old profile picture file old_picture_path = os.path.join(UPLOAD_FOLDER, current_user.profile_picture) if os.path.exists(old_picture_path): os.remove(old_picture_path) current_user.profile_picture = None db.session.commit() flash('Profile picture removed successfully!', 'success') return redirect(url_for('main.profile')) new_email = request.form.get('email') # Check if the new email is already used by another user if new_email != current_user.email: existing_user = User.query.filter_by(email=new_email).first() if existing_user: flash('A user with this email already exists.', 'error') return render_template('profile.html') # Handle profile picture upload file = request.files.get('profile_picture') if file and file.filename: filename = secure_filename(file.filename) file_path = os.path.join(UPLOAD_FOLDER, filename) file.save(file_path) current_user.profile_picture = filename # Update user information current_user.username = request.form.get('first_name') current_user.last_name = request.form.get('last_name') current_user.email = new_email current_user.phone = request.form.get('phone') current_user.company = request.form.get('company') current_user.position = request.form.get('position') current_user.notes = request.form.get('notes') # Handle password change if provided new_password = request.form.get('new_password') confirm_password = request.form.get('confirm_password') if new_password: if new_password != confirm_password: flash('Passwords do not match.', 'error') return render_template('profile.html') current_user.set_password(new_password) flash('Password updated successfully.', 'success') try: db.session.commit() flash('Profile updated successfully!', 'success') except Exception as e: db.session.rollback() flash('An error occurred while updating your profile.', 'error') return redirect(url_for('main.profile')) return render_template('profile.html') @main_bp.route('/starred') @login_required def starred(): return render_template('starred.html') @main_bp.route('/trash') @login_required def trash(): return render_template('trash.html')