Repository: ElliceNels/fourohfour
Pull request #10: Create permission and file endpoints
Author: login: jeancarson
Created: 2025-05-20T20:24:54Z | Merged: 2025-05-22T16:58:31Z

Added endpoints for file sharing and permission handling. 
Databse interaction's should match @ElliceNels recent ORM code. 

Note: These routes assume:
- A get_current_user() function will be implemented for authentication
- Database connection (db) will be properly configured
- Uploads directory will be set up for file storage


Diff:
diff --git a/.gitignore b/.gitignore
index 38f66cca..62bf6f94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,4 +18,4 @@ libsodium-*
 # Ignore virtual environments
 venv/
 env/
-ENV/
\ No newline at end of file
+ENV/
diff --git a/src/server/routes/file_routes.py b/src/server/routes/file_routes.py
new file mode 100644
index 00000000..b0eb51b1
--- /dev/null
+++ b/src/server/routes/file_routes.py
@@ -0,0 +1,123 @@
+import base64
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user
+from ..utils.file_utils import upload_file_to_db, get_user_files, get_file_by_id, delete_file_by_id
+from werkzeug.utils import secure_filename
+import os
+from datetime import datetime, UTC
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """Upload a new file to the system.
+    
+    Expected request:
+    - encrypted_file: The encrypted file data in request.files
+    - metadata: JSON object containing:
+        - size: File size in bytes
+        - format: File format/extension
+
+    Returns:
+    {
+        "message": "File uploaded successfully",
+        "file_id": <file_id>
+    }
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Save the file to disk
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"
+        file_path = os.path.join('uploads', filename)
+        file.save(file_path)
+
+        return upload_file_to_db(current_user.id, file, file_path, request.metadata)
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """List all files accessible to the authenticated user.
+    
+    Returns:
+    {
+        "owned_files": [
+            {
+                "id": <file_id>,
+                "filename": <filename>,
+                "file_size": <size>,
+                "format": <format>,
+                "uploaded_at": <timestamp>,
+                "is_owner": true
+            }
+        ],
+        "shared_files": [
+            {
+                "id": <file_id>,
+                "filename": <filename>,
+                "file_size": <size>,
+                "format": <format>,
+                "uploaded_at": <timestamp>,
+                "is_owner": false
+            }
+        ]
+    }
+    """
+    try:
+        current_user = get_current_user()
+        return get_user_files(current_user.id)
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/<file_id>', methods=['GET'])
+def get_file(file_id):
+    """Get a specific file by ID.
+    
+    Args:
+        file_id: The ID of the requested file
+
+    Returns:
+    {
+        "encrypted_file": <base64_encoded_file_data>,
+        "encrypted_keys": {  # Only included if user is owner
+            <user_id>: <encryption_key>
+        }
+    }
+    """
+    try:
+        current_user = get_current_user()
+        return get_file_by_id(file_id, current_user.id)
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/<file_id>', methods=['DELETE'])
+def delete_file(file_id):
+    """Delete a specific file by ID.
+    
+    Args:
+        file_id: The ID of the file to delete
+
+    Returns:
+    {
+        "message": "File deleted successfully"
+    }
+    """
+    try:
+        current_user = get_current_user()
+        return delete_file_by_id(file_id, current_user.id)
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
\ No newline at end of file
diff --git a/src/server/routes/permission_routes.py b/src/server/routes/permission_routes.py
new file mode 100644
index 00000000..cbdc2d50
--- /dev/null
+++ b/src/server/routes/permission_routes.py
@@ -0,0 +1,98 @@
+from flask import Blueprint, jsonify, request
+from ..utils.auth import get_current_user
+from ..utils.permission_utils import get_user_public_key, create_file_permission, remove_file_permission
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """Get the public key of a user.
+    
+    Query Parameters:
+        user_id: The ID of the user whose public key is requested
+
+    Returns:
+    {
+        "public_key": "<user_public_key>"
+    }
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        return get_user_public_key(int(user_id))
+    except ValueError:
+        return jsonify({'error': 'Invalid user_id format'}), 400
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['POST'])
+def create_permission():
+    """Create a new file permission.
+    
+    Expected JSON payload:
+    {
+        "file_id": <file_id>,
+        "user_id": <user_id>,
+        "key_for_recipient": "<encrypted_symmetric_key>"
+    }
+
+    Returns:
+    {
+        "message": "Permission created successfully"
+    }
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id', 'key_for_recipient']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        return create_file_permission(
+            data['file_id'],
+            data['user_id'],
+            data['key_for_recipient'],
+            current_user.id
+        )
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['DELETE'])
+def remove_permission():
+    """Remove a file permission.
+    
+    Expected JSON payload:
+    {
+        "file_id": <file_id>,
+        "user_id": <user_id>
+    }
+
+    Returns:
+    {
+        "message": "Permission removed successfully"
+    }
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        return remove_file_permission(
+            data['file_id'],
+            data['user_id'],
+            current_user.id
+        )
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
\ No newline at end of file
diff --git a/src/server/utils/file_utils.py b/src/server/utils/file_utils.py
new file mode 100644
index 00000000..985086d4
--- /dev/null
+++ b/src/server/utils/file_utils.py
@@ -0,0 +1,192 @@
+from datetime import datetime, UTC
+from flask import jsonify
+from werkzeug.utils import secure_filename
+import os
+import base64
+from ..models.tables import Files, FilePermissions, FileMetadata
+from ..app import Session
+
+def upload_file_to_db(user_id: int, file, file_path: str, metadata: dict) -> dict:
+    """Upload a file to the database and disk storage.
+
+    Args:
+        user_id (int): ID of the user uploading the file
+        file: The file object from request.files
+        file_path (str): Path where the file will be stored
+        metadata (dict): Dictionary containing file metadata (size, format)
+
+    Returns:
+        dict: Response containing success message and file ID or error message
+    """
+    db = Session()
+    try:
+        # Create database entry
+        new_file = Files(
+            owner_id=user_id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.now(UTC)
+        )
+        db.add(new_file)
+        db.flush()
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=metadata['size'],
+            format=metadata['format'],
+            last_updated_at=datetime.now(UTC)
+        )
+        db.add(file_metadata)
+        
+        db.commit()
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        db.rollback()
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()
+
+def get_user_files(user_id: int) -> dict:
+    """Get all files accessible to a user (owned and shared).
+
+    Args:
+        user_id (int): ID of the user requesting files
+
+    Returns:
+        dict: Response containing lists of owned and shared files
+    """
+    db = Session()
+    try:
+        # Get files owned by the user
+        owned_files = db.query(Files).filter_by(owner_id=user_id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'format': file.metadata.format if file.metadata else None,
+            'uploaded_at': file.uploaded_at.isoformat() if file.uploaded_at else None,
+            'is_owner': True
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = db.query(FilePermissions).filter_by(user_id=user_id).all()
+        shared_file_ids = [permission.file_id for permission in shared_permissions]
+        shared_files = db.query(Files).filter(Files.id.in_(shared_file_ids)).all()
+        shared_files_map = {file.id: file for file in shared_files}
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = shared_files_map.get(permission.file_id)
+            if file:
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'format': file.metadata.format if file.metadata else None,
+                    'uploaded_at': file.uploaded_at.isoformat() if file.uploaded_at else None,
+                    'is_owner': False
+                })
+
+        return jsonify({
+            'owned_files': owned_files_data,
+            'shared_files': shared_files_data
+        })
+
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()
+
+def get_file_by_id(file_id: int, user_id: int) -> dict:
+    """Get a specific file by ID if user has access.
+
+    Args:
+        file_id (int): ID of the requested file
+        user_id (int): ID of the user requesting the file
+
+    Returns:
+        dict: Response containing file data and sharing keys if user is owner
+    """
+    db = Session()
+    try:
+        # Find the file
+        file = db.query(Files).get(file_id)
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+
+        # Check if user has access
+        if file.owner_id != user_id:
+            permission = db.query(FilePermissions).filter_by(
+                file_id=file_id,
+                user_id=user_id
+            ).first()
+            if not permission:
+                return jsonify({'error': 'Not authorized to access this file'}), 403
+
+        # Read the encrypted file
+        try:
+            with open(file.path, 'rb') as f:
+                encrypted_file = f.read()
+        except Exception as e:
+            return jsonify({'error': 'Error reading file'}), 500
+
+        response_data = {
+            'encrypted_file': base64.b64encode(encrypted_file).decode('utf-8')
+        }
+
+        # If user is owner, include all sharing keys
+        if file.owner_id == user_id:
+            permissions = db.query(FilePermissions).filter_by(file_id=file_id).all()
+            response_data['encrypted_keys'] = {
+                perm.user_id: perm.encryption_key for perm in permissions
+            }
+
+        return jsonify(response_data)
+
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()
+
+def delete_file_by_id(file_id: int, user_id: int) -> dict:
+    """Delete a file if user is the owner.
+
+    Args:
+        file_id (int): ID of the file to delete
+        user_id (int): ID of the user requesting deletion
+
+    Returns:
+        dict: Response containing success message or error
+    """
+    db = Session()
+    try:
+        # Find the file
+        file = db.query(Files).get(file_id)
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+
+        # Verify ownership
+        if file.owner_id != user_id:
+            return jsonify({'error': 'Not authorized to delete this file'}), 403
+
+        # Delete the file from disk
+        try:
+            os.remove(file.path)
+        except Exception as e:
+            return jsonify({'error': 'Error deleting file from disk'}), 500
+
+        # Delete from database
+        db.delete(file)
+        db.commit()
+
+        return jsonify({'message': 'File deleted successfully'})
+
+    except Exception as e:
+        db.rollback()
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()
diff --git a/src/server/utils/permission_utils.py b/src/server/utils/permission_utils.py
new file mode 100644
index 00000000..79b88b25
--- /dev/null
+++ b/src/server/utils/permission_utils.py
@@ -0,0 +1,121 @@
+from datetime import datetime, UTC
+from flask import jsonify
+from ..models.tables import Users, Files, FilePermissions
+from ..app import Session
+
+def get_user_public_key(user_id: int) -> dict:
+    """Get the public key of a user.
+
+    Args:
+        user_id (int): ID of the user whose public key is requested
+
+    Returns:
+        dict: Response containing the user's public key or error message
+    """
+    db = Session()
+    try:
+        user = db.query(Users).get(user_id)
+        if not user:
+            return jsonify({'error': 'User not found'}), 404
+
+        return jsonify({
+            'public_key': user.public_key
+        })
+
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()
+
+def create_file_permission(file_id: int, user_id: int, key_for_recipient: str, owner_id: int) -> dict:
+    """Create a new file permission for a user.
+
+    Args:
+        file_id (int): ID of the file to share
+        user_id (int): ID of the user to share with
+        key_for_recipient (str): Symmetric file key encrypted with the recipient's public key
+        owner_id (int): ID of the file owner
+
+    Returns:
+        dict: Response containing success message or error
+    """
+    db = Session()
+    try:
+        # Check if the file exists and belongs to the owner
+        file = db.query(Files).get(file_id)
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != owner_id:
+            return jsonify({'error': 'Not authorized to share this file'}), 403
+
+        # Check if the recipient user exists
+        recipient = db.query(Users).get(user_id)
+        if not recipient:
+            return jsonify({'error': 'Recipient user not found'}), 404
+
+        # Check if permission already exists
+        existing_permission = db.query(FilePermissions).filter_by(
+            file_id=file_id,
+            user_id=user_id
+        ).first()
+        if existing_permission:
+            return jsonify({'error': 'Permission already exists'}), 409
+
+        # Create new permission
+        new_permission = FilePermissions(
+            file_id=file_id,
+            user_id=user_id,
+            encryption_key=key_for_recipient,
+            created_at=datetime.now(UTC),
+            updated_at=datetime.now(UTC)
+        )
+        db.add(new_permission)
+        db.commit()
+
+        return jsonify({'message': 'Permission created successfully'}), 201
+
+    except Exception as e:
+        db.rollback()
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()
+
+def remove_file_permission(file_id: int, user_id: int, owner_id: int) -> dict:
+    """Remove a file permission for a user.
+
+    Args:
+        file_id (int): ID of the file to remove permission from
+        user_id (int): ID of the user to remove permission for
+        owner_id (int): ID of the file owner
+
+    Returns:
+        dict: Response containing success message or error
+    """
+    db = Session()
+    try:
+        # Check if the file exists and belongs to the owner
+        file = db.query(Files).get(file_id)
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != owner_id:
+            return jsonify({'error': 'Not authorized to modify permissions for this file'}), 403
+
+        # Find and delete the permission
+        permission = db.query(FilePermissions).filter_by(
+            file_id=file_id,
+            user_id=user_id
+        ).first()
+        
+        if not permission:
+            return jsonify({'error': 'Permission not found'}), 404
+
+        db.delete(permission)
+        db.commit()
+
+        return jsonify({'message': 'Permission removed successfully'}), 200
+
+    except Exception as e:
+        db.rollback()
+        return jsonify({'error': str(e)}), 500
+    finally:
+        db.close()


Reviews:
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T20:25:55Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR introduces skeleton routes for managing permissions and files, and hooks them into the Flask app.

- Added `permission_routes.py` with three placeholder endpoints (`/public_key`, `/add`, `/remove`)  
- Added `file_routes.py` with CRUD placeholders for file upload, listing, retrieval, and deletion  
- Updated `app.py` to fix the authentication import, register the new blueprints, and add a root index route

### Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

| File                                | Description                                           |
| ----------------------------------- | ----------------------------------------------------- |
| src/server/routes/permission_routes.py | New blueprint for permission-related endpoints        |
| src/server/routes/file_routes.py       | New blueprint for file upload, listing, retrieval, and deletion |
| src/server/app.py                     | Fixed import path, registered `permission_bp` & `files_bp`, added `/` index route |


<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**src/server/routes/file_routes.py:12**
* This new endpoint lacks any accompanying test coverage. Please add unit or integration tests to verify successful uploads and error conditions.
```
# TODO: Store the file on the server
```
**src/server/routes/file_routes.py:61**
* This always returns a success response without performing the authorization checks mentioned in the TODOs. Ensure you verify the user's ownership or permissions before deleting the file.
```
return jsonify({"message": "File deleted successfully"}), 200
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T20:28:06Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR adds REST endpoints to manage file uploads, metadata, and sharing permissions, and registers them in the Flask app.

- Introduces routes in `permission_routes.py` to fetch public keys and create/remove file permissions.
- Implements file operations in `file_routes.py` (upload, list, retrieve, delete).
- Registers the new blueprints and adds a basic index route in `app.py`.

### Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

| File                             | Description                                           |
|----------------------------------|-------------------------------------------------------|
| src/server/routes/permission_routes.py | Adds endpoints for getting public keys and managing file permissions. |
| src/server/routes/file_routes.py       | Implements file upload, listing, retrieval, and deletion. |
| src/server/app.py                     | Imports and registers new permission and file blueprints; adds index route. |


<details>
<summary>Comments suppressed due to low confidence (4)</summary>

**src/server/routes/permission_routes.py:3**
* [nitpick] Remove the placeholder comment `Spoiler, we don't`. It can confuse readers and should be replaced with a proper TODO or removed once implemented.
```
from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
```
**src/server/routes/permission_routes.py:79**
* The `db` object is not imported in this module, which will cause a `NameError` at runtime. Add `from ..models import db` or the correct import.
```
db.session.add(new_permission)
```
**src/server/routes/file_routes.py:36**
* The `db` object is not imported in this module, causing a `NameError`. Import `db` from your application or models.
```
db.session.add(new_file)
```
**src/server/app.py:5**
* Verify that the module `routes.authentication_routes` matches your file names. A typo here may lead to an ImportError at startup.
```
from routes.authentication_routes import authentication_routes as auth_bp
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T21:57:59Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T22:01:16Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T22:08:19Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-21T22:09:06Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:29:01Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:30:36Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:31:03Z
user: login: ElliceNels
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-22T09:32:30Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:32:55Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:35:14Z
user: login: ElliceNels
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-22T09:35:40Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:36:07Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:36:15Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:37:15Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:37:43Z
user: login: ElliceNels
state: COMMENTED
author_association: OWNER
submitted_at: 2025-05-22T09:37:46Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:38:18Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:38:31Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T09:40:42Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T10:18:55Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T11:31:46Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T11:33:19Z
user: login: Kabidoye-17
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T11:34:03Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T11:39:14Z
user: login: jeancarson
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T12:22:36Z
user: login: ElliceNels
state: APPROVED
author_association: OWNER
submitted_at: 2025-05-22T16:57:57Z

Review comments:
diff_hunk: @@ -2,7 +2,9 @@
 from flask_cors import CORS
 from dotenv import load_dotenv
 import os
-from .routes import authenication_routes as auth_bp
+from routes.authentication_routes import authentication_routes as auth_bp
path: src/server/app.py
user: login: jeancarson
body: happy to change this back to `from .routes` but I couldn't get the flask server to run. Might be doing something wrong
created_at: 2025-05-20T20:25:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,41 @@
+from flask import Blueprint, jsonify, request
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    #TODO : Look up the user_id in the database, return their public key
+    return 
path: src/server/routes/permission_routes.py
user: login: Copilot
body: These handlers currently return `None`, which will result in a 500 error. Consider returning a placeholder JSON response (e.g., `jsonify({'message': 'Not implemented'})`) or raising a `NotImplementedError` until the logic is implemented.
```suggestion
    return jsonify({'message': 'Not implemented'})
```
created_at: 2025-05-20T20:28:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+from flask import Blueprint, jsonify, request
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    #TODO : Look up the user_id in the database, return their public key
+    return 
+
+@permission_bp.route('/add', methods=['POST'])
+def add_permission():
+    """
+    Adds a permission to a file
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    - key_for_recipient: The symmetric file key encrypted with the user_id public key
+    """
+    #TODO: Check if the user making the request is the owner of the file
+    #TODO : Check if the file_id and user_id exist
+    #TODO: Add a filePermission entry to the database
+
+    return
+
+@permission_bp.route('/remove', methods=['POST'])
+def remove_permission():
+    """
+    Removes a permission from a file
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    """
+    #TODO: Check if the user making the request is the owner of the file
+    #TODO check if the file_id and user_id exist
+    #TODO: Remove the filePermissoin entry from the database
path: src/server/routes/permission_routes.py
user: login: Copilot
body: Typo in 'filePermissoin'; it should be 'filePermission'.
```suggestion
    #TODO: Remove the filePermission entry from the database
```
created_at: 2025-05-20T20:28:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+from flask import Blueprint, jsonify, request
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    # TODO: Store the file on the server
+    #TODO Add an entry to the file database, inlcuding file location etc
path: src/server/routes/file_routes.py
user: login: Copilot
body: Typo in 'inlcuding'; it should be 'including'.
```suggestion
    #TODO Add an entry to the file database, including file location etc
```
created_at: 2025-05-20T20:28:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+from flask import Blueprint, jsonify, request
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    # TODO: Store the file on the server
+    #TODO Add an entry to the file database, inlcuding file location etc
+    return 
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    -keys: A list of encrypted keys
+    """
+    # TODO: Query database for user's files
+    # TODO: Find all files relating to user, both in the file database and the filePermission database
+    # TODO: Check if the user and file have a corresponding entry in the filePermission database, or are the owner
+    # TODO: If owner, send only encryted file
+    # TODO: If shared, send both encrypted file and encrypted key
+    return
+
+
+@files_bp.route('/<file_id>', methods=['GET'])
+def get_file(file_id):
+    """
+    Get a specific file by ID
+    Expects:
+    - file_id: The ID of the file
+    Returns:
+    - encrypted_file: The encrypted file data
+    - encrypted_keys: The encrypted keys for sharing
+    """
+    # TODO: Get user ID from authentication
+    # TODO: Find the file in the database
+    # TODO  : Check if the user and file have a correspoinding entry in the filePermission database
path: src/server/routes/file_routes.py
user: login: Copilot
body: Typo in 'correspoinding'; it should be 'corresponding'.
```suggestion
    # TODO  : Check if the user and file have a corresponding entry in the filePermission database
```
created_at: 2025-05-20T20:28:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+from flask import Blueprint, jsonify, request
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
path: src/server/routes/permission_routes.py
user: login: Copilot
body: The docstring mentions `user_id` but doesn't specify how it should be provided (query parameter, header, or body). Clarify the expected input method.
```suggestion
    Get the public key of a user.
    Expects:
    - user_id: The ID of the user (provided as a query parameter, e.g., /public_key?user_id=<id>)
```
created_at: 2025-05-20T20:28:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,41 @@
+from flask import Blueprint, jsonify, request
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    #TODO : Look up the user_id in the database, return their public key
+    return 
+
+@permission_bp.route('/add', methods=['POST'])
+def add_permission():
+    """
+    Adds a permission to a file
path: src/server/routes/permission_routes.py
user: login: Copilot
body: [nitpick] Using verbs in your route (e.g., `/add`) deviates from typical REST conventions. Consider POSTing to `/api/permissions` instead, and DELETEing to `/api/permissions/{id}` for a more RESTful design.
```suggestion
@permission_bp.route('', methods=['POST'])
def create_permission():
    """
    Creates a new permission for a file
```
created_at: 2025-05-20T20:28:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
path: src/server/routes/file_routes.py
user: login: Copilot
body: Unsanitized `file.filename` could allow path traversal or overwrite. Sanitize the filename or use a safe random name.
```suggestion
        sanitized_filename = secure_filename(file.filename)
        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
```
created_at: 2025-05-21T21:57:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
path: src/server/routes/file_routes.py
user: login: Copilot
body: `new_file.id` may be `None` until the session is flushed or committed, causing a foreign key error. Call `db.session.flush()` before using the generated ID.
created_at: 2025-05-21T21:57:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=os.path.getsize(file_path),
+            format=file.filename.split('.')[-1] if '.' in file.filename else 'unknown',
+            last_updated_at=datetime.utcnow()
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    - keys: A list of encrypted keys
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'is_owner': True,
+            'encrypted_file': file.path
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = Files.query.get(permission.file_id)
path: src/server/routes/file_routes.py
user: login: Copilot
body: Fetching each file inside the loop causes an N+1 query pattern. Consider joining `Files` in the permissions query or batch fetching.
```suggestion
        shared_file_ids = [permission.file_id for permission in shared_permissions]
        shared_files = Files.query.filter(Files.id.in_(shared_file_ids)).all()
        shared_files_map = {file.id: file for file in shared_files}
        shared_files_data = []
        for permission in shared_permissions:
            file = shared_files_map.get(permission.file_id)
```
created_at: 2025-05-21T21:57:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=os.path.getsize(file_path),
+            format=file.filename.split('.')[-1] if '.' in file.filename else 'unknown',
+            last_updated_at=datetime.utcnow()
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    - keys: A list of encrypted keys
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'is_owner': True,
+            'encrypted_file': file.path
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = Files.query.get(permission.file_id)
+            if file:  # File might have been deleted
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'is_owner': False,
+                    'encrypted_file': file.path,
+                    'encrypted_key': permission.encryption_key
+                })
+
+        return jsonify({
+            'files': owned_files_data + shared_files_data
+        })
+
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/<file_id>', methods=['GET'])
+def get_file(file_id):
+    """
+    Get a specific file by ID
+    Expects:
+    - file_id: The ID of the file
+    Returns:
+    - encrypted_file: The encrypted file data
+    - encrypted_keys: The encrypted keys for sharing
+    """
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Find the file
+        file = Files.query.get(file_id)
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+
+        # Check if user has access
+        if file.owner_id != current_user.id:
+            # Check if file is shared with user
+            permission = FilePermissions.query.filter_by(
+                file_id=file_id,
+                user_id=current_user.id
+            ).first()
+            if not permission:
+                return jsonify({'error': 'Not authorized to access this file'}), 403
+
+        # Read the encrypted file
+        try:
+            with open(file.path, 'rb') as f:
+                encrypted_file = f.read()
+        except Exception as e:
+            return jsonify({'error': 'Error reading file'}), 500
+
+        response_data = {
+            'encrypted_file': encrypted_file
path: src/server/routes/file_routes.py
user: login: Copilot
body: Returning raw binary data in JSON may not serialize correctly. Consider returning a base64‐encoded string or sending the file as an attachment.
```suggestion
            'encrypted_file': base64.b64encode(encrypted_file).decode('utf-8')
```
created_at: 2025-05-21T21:57:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
path: src/server/routes/file_routes.py
user: login: jeancarson
body: ooh thats a really good point for ethical hacking

created_at: 2025-05-21T22:01:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
path: src/server/routes/file_routes.py
user: login: jeancarson
body: solved in next commit

created_at: 2025-05-21T22:08:19Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,192 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        filename = f"{current_user.id}_{file.filename}"  # Assuming we want to prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=os.path.getsize(file_path),
+            format=file.filename.split('.')[-1] if '.' in file.filename else 'unknown',
+            last_updated_at=datetime.utcnow()
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    - keys: A list of encrypted keys
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'is_owner': True,
+            'encrypted_file': file.path
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = Files.query.get(permission.file_id)
+            if file:  # File might have been deleted
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'is_owner': False,
+                    'encrypted_file': file.path,
+                    'encrypted_key': permission.encryption_key
+                })
+
+        return jsonify({
+            'files': owned_files_data + shared_files_data
+        })
+
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/<file_id>', methods=['GET'])
+def get_file(file_id):
+    """
+    Get a specific file by ID
+    Expects:
+    - file_id: The ID of the file
+    Returns:
+    - encrypted_file: The encrypted file data
+    - encrypted_keys: The encrypted keys for sharing
+    """
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Find the file
+        file = Files.query.get(file_id)
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+
+        # Check if user has access
+        if file.owner_id != current_user.id:
+            # Check if file is shared with user
+            permission = FilePermissions.query.filter_by(
+                file_id=file_id,
+                user_id=current_user.id
+            ).first()
+            if not permission:
+                return jsonify({'error': 'Not authorized to access this file'}), 403
+
+        # Read the encrypted file
+        try:
+            with open(file.path, 'rb') as f:
+                encrypted_file = f.read()
+        except Exception as e:
+            return jsonify({'error': 'Error reading file'}), 500
+
+        response_data = {
+            'encrypted_file': encrypted_file
path: src/server/routes/file_routes.py
user: login: jeancarson
body: suggestion taken- resolved in next commit
created_at: 2025-05-21T22:09:06Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: what does this do ?
created_at: 2025-05-22T09:29:01Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: we file name will be passed in seperately not as file.filename, since the file is encryted
created_at: 2025-05-22T09:30:36Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        db.session.flush() #so new file record is generated before we reference it below in metadata.
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: this is all being passed in from the frontend for integrity 
created_at: 2025-05-22T09:31:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,131 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Users, Files, FilePermissions 
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from datetime import datetime
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        user = Users.query.get(user_id)
path: src/server/routes/permission_routes.py
user: login: ElliceNels
body: do these queries work?
created_at: 2025-05-22T09:32:30Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        db.session.flush() #so new file record is generated before we reference it below in metadata.
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=os.path.getsize(file_path),
+            format=file.filename.split('.')[-1] if '.' in file.filename else 'unknown',
+            last_updated_at=datetime.utcnow()
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    - keys: A list of encrypted keys
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'is_owner': True,
+            'encrypted_file': file.path
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_file_ids = [permission.file_id for permission in shared_permissions]
+        shared_files = Files.query.filter(Files.id.in_(shared_file_ids)).all()
+        shared_files_map = {file.id: file for file in shared_files}
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = shared_files_map.get(permission.file_id)
+            if file:  # File might have been deleted
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'is_owner': False,
+                    'encrypted_file': file.path,
+                    'encrypted_key': permission.encryption_key
+                })
+
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: is there a chance you could return them in two seperate variables?
so i can seperate them out on the frontend
created_at: 2025-05-22T09:32:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
path: src/server/routes/file_routes.py
user: login: jeancarson
body: This was a change made after a copilot suggestion - apparently to stop an attacker being able to find the file via path traversal? BUT this function was actually not imported 🤦‍♀️WIll be in the next commit
> Unsanitized file.filename could allow path traversal or overwrite. Sanitize the filename or use a safe random name.
created_at: 2025-05-22T09:35:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,131 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Users, Files, FilePermissions 
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from datetime import datetime
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        user = Users.query.get(user_id)
+        if not user:
+            return jsonify({'error': 'User not found'}), 404
+
+        return jsonify({
+            'public_key': user.public_key
+        })
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['POST'])
+def create_permission():
+    """
+    Creates a new permission
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    - key_for_recipient: The symmetric file key encrypted with the user_id public key
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id', 'key_for_recipient']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Check if the file exists and belongs to the current user
+        file = Files.query.get(data['file_id'])
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != current_user.id:
+            return jsonify({'error': 'Not authorized to share this file'}), 403
+
+        # Check if the recipient user exists
+        recipient = Users.query.get(data['user_id'])
+        if not recipient:
+            return jsonify({'error': 'Recipient user not found'}), 404
+
+        # Check if permission already exists
+        existing_permission = FilePermissions.query.filter_by(
+            file_id=data['file_id'],
+            user_id=data['user_id']
+        ).first()
+        if existing_permission:
+            return jsonify({'error': 'Permission already exists'}), 409
+
+        # Create new permission
+        new_permission = FilePermissions(
+            file_id=data['file_id'],
+            user_id=data['user_id'],
+            encryption_key=data['key_for_recipient'],
+            created_at=datetime.utcnow(),
+            updated_at=datetime.utcnow()
+        )
+        db.session.add(new_permission)
path: src/server/routes/permission_routes.py
user: login: ElliceNels
body: where is db.session defined
created_at: 2025-05-22T09:35:40Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: jeancarson
body: OOh good catch - I suspect this should be sanitized_filename - wasn't changed after making the above change. Will fix
created_at: 2025-05-22T09:36:07Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        db.session.flush() #so new file record is generated before we reference it below in metadata.
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=os.path.getsize(file_path),
+            format=file.filename.split('.')[-1] if '.' in file.filename else 'unknown',
+            last_updated_at=datetime.utcnow()
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    - keys: A list of encrypted keys
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'is_owner': True,
+            'encrypted_file': file.path
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_file_ids = [permission.file_id for permission in shared_permissions]
+        shared_files = Files.query.filter(Files.id.in_(shared_file_ids)).all()
+        shared_files_map = {file.id: file for file in shared_files}
+        shared_files_data = []
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: Also my though process going to be that for all files (owned and shared), we would just fetch the metadata for all, then when the user decides the want to open a specific file then we fetch the encrypted file and the encrypted key if necessary? so were not loading unnecessary files.
created_at: 2025-05-22T09:36:15Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,131 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Users, Files, FilePermissions 
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from datetime import datetime
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        user = Users.query.get(user_id)
path: src/server/routes/permission_routes.py
user: login: jeancarson
body: I don't think I've been able to check until we have an actual db connection. If I can please lmk. But they are designed to fit in with the ORM code
created_at: 2025-05-22T09:37:15Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.utcnow()
+        )
+        db.session.add(new_file)
+        db.session.flush() #so new file record is generated before we reference it below in metadata.
+        
+        # Create metadata entry
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=os.path.getsize(file_path),
+            format=file.filename.split('.')[-1] if '.' in file.filename else 'unknown',
+            last_updated_at=datetime.utcnow()
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of encrypted files
+    - keys: A list of encrypted keys
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'is_owner': True,
+            'encrypted_file': file.path
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_file_ids = [permission.file_id for permission in shared_permissions]
+        shared_files = Files.query.filter(Files.id.in_(shared_file_ids)).all()
+        shared_files_map = {file.id: file for file in shared_files}
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = shared_files_map.get(permission.file_id)
+            if file:  # File might have been deleted
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'is_owner': False,
+                    'encrypted_file': file.path,
+                    'encrypted_key': permission.encryption_key
+                })
+
path: src/server/routes/file_routes.py
user: login: jeancarson
body: I don't understand - what format would be preferred?

created_at: 2025-05-22T09:37:43Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,131 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Users, Files, FilePermissions 
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from datetime import datetime
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        user = Users.query.get(user_id)
+        if not user:
+            return jsonify({'error': 'User not found'}), 404
+
+        return jsonify({
+            'public_key': user.public_key
+        })
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['POST'])
+def create_permission():
+    """
+    Creates a new permission
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    - key_for_recipient: The symmetric file key encrypted with the user_id public key
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id', 'key_for_recipient']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Check if the file exists and belongs to the current user
+        file = Files.query.get(data['file_id'])
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != current_user.id:
+            return jsonify({'error': 'Not authorized to share this file'}), 403
+
+        # Check if the recipient user exists
+        recipient = Users.query.get(data['user_id'])
+        if not recipient:
+            return jsonify({'error': 'Recipient user not found'}), 404
+
+        # Check if permission already exists
+        existing_permission = FilePermissions.query.filter_by(
+            file_id=data['file_id'],
+            user_id=data['user_id']
+        ).first()
+        if existing_permission:
+            return jsonify({'error': 'Permission already exists'}), 409
+
+        # Create new permission
+        new_permission = FilePermissions(
+            file_id=data['file_id'],
+            user_id=data['user_id'],
+            encryption_key=data['key_for_recipient'],
+            created_at=datetime.utcnow(),
+            updated_at=datetime.utcnow()
+        )
+        db.session.add(new_permission)
+        db.session.commit()
+
+        return jsonify({'message': 'Permission created successfully'}), 201
+
+    except Exception as e:
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['DELETE'])
+def remove_permission():
+    """
+    Removes a permission from a file
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Check if the file exists and belongs to the current user
+        file = Files.query.get(data['file_id'])
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != current_user.id:
+            return jsonify({'error': 'Not authorized to modify permissions for this file'}), 403
+
+        # Find and delete the permission
+        permission = FilePermissions.query.filter_by(
+            file_id=data['file_id'],
+            user_id=data['user_id']
+        ).first()
+        
+        if not permission:
+            return jsonify({'error': 'Permission not found'}), 404
+
+        db.session.delete(permission)
+        db.session.commit()
path: src/server/routes/permission_routes.py
user: login: ElliceNels
body: sessions probably need to be closed after finishing
created_at: 2025-05-22T09:37:46Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: is there a way to reverse the sanitisation cause it will cause integrity errors if  the filename is  changed even slightly
created_at: 2025-05-22T09:38:18Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,131 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Users, Files, FilePermissions 
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from datetime import datetime
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        user = Users.query.get(user_id)
+        if not user:
+            return jsonify({'error': 'User not found'}), 404
+
+        return jsonify({
+            'public_key': user.public_key
+        })
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['POST'])
+def create_permission():
+    """
+    Creates a new permission
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    - key_for_recipient: The symmetric file key encrypted with the user_id public key
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id', 'key_for_recipient']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Check if the file exists and belongs to the current user
+        file = Files.query.get(data['file_id'])
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != current_user.id:
+            return jsonify({'error': 'Not authorized to share this file'}), 403
+
+        # Check if the recipient user exists
+        recipient = Users.query.get(data['user_id'])
+        if not recipient:
+            return jsonify({'error': 'Recipient user not found'}), 404
+
+        # Check if permission already exists
+        existing_permission = FilePermissions.query.filter_by(
+            file_id=data['file_id'],
+            user_id=data['user_id']
+        ).first()
+        if existing_permission:
+            return jsonify({'error': 'Permission already exists'}), 409
+
+        # Create new permission
+        new_permission = FilePermissions(
+            file_id=data['file_id'],
+            user_id=data['user_id'],
+            encryption_key=data['key_for_recipient'],
+            created_at=datetime.utcnow(),
+            updated_at=datetime.utcnow()
+        )
+        db.session.add(new_permission)
+        db.session.commit()
+
+        return jsonify({'message': 'Permission created successfully'}), 201
+
+    except Exception as e:
+        db.session.rollback()
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['DELETE'])
+def remove_permission():
+    """
+    Removes a permission from a file
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Check if the file exists and belongs to the current user
+        file = Files.query.get(data['file_id'])
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != current_user.id:
+            return jsonify({'error': 'Not authorized to modify permissions for this file'}), 403
+
+        # Find and delete the permission
+        permission = FilePermissions.query.filter_by(
+            file_id=data['file_id'],
+            user_id=data['user_id']
+        ).first()
+        
+        if not permission:
+            return jsonify({'error': 'Permission not found'}), 404
+
+        db.session.delete(permission)
+        db.session.commit()
path: src/server/routes/permission_routes.py
user: login: jeancarson
body: Truuuu! WIll change thank you
created_at: 2025-05-22T09:38:31Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,131 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Users, Files, FilePermissions 
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from datetime import datetime
+
+permission_bp = Blueprint('permissions', __name__, url_prefix='/api/permissions')
+
+@permission_bp.route('/public_key', methods=['GET'])
+def get_public_key():
+    """
+    Get the public key of a user
+    Expects:
+    - user_id: The ID of the user
+    """
+    user_id = request.args.get('user_id')
+    if not user_id:
+        return jsonify({'error': 'user_id is required'}), 400
+
+    try:
+        user = Users.query.get(user_id)
+        if not user:
+            return jsonify({'error': 'User not found'}), 404
+
+        return jsonify({
+            'public_key': user.public_key
+        })
+    except Exception as e:
+        return jsonify({'error': str(e)}), 500
+
+@permission_bp.route('', methods=['POST'])
+def create_permission():
+    """
+    Creates a new permission
+    Expects:
+    - file_id: The ID of the file
+    - user_id: The ID of the user
+    - key_for_recipient: The symmetric file key encrypted with the user_id public key
+    """
+    data = request.get_json()
+    if not data:
+        return jsonify({'error': 'No data provided'}), 400
+
+    required_fields = ['file_id', 'user_id', 'key_for_recipient']
+    for field in required_fields:
+        if field not in data:
+            return jsonify({'error': f'{field} is required'}), 400
+
+    try:
+        current_user = get_current_user()
+        
+        # Check if the file exists and belongs to the current user
+        file = Files.query.get(data['file_id'])
+        if not file:
+            return jsonify({'error': 'File not found'}), 404
+        if file.owner_id != current_user.id:
+            return jsonify({'error': 'Not authorized to share this file'}), 403
+
+        # Check if the recipient user exists
+        recipient = Users.query.get(data['user_id'])
+        if not recipient:
+            return jsonify({'error': 'Recipient user not found'}), 404
+
+        # Check if permission already exists
+        existing_permission = FilePermissions.query.filter_by(
+            file_id=data['file_id'],
+            user_id=data['user_id']
+        ).first()
+        if existing_permission:
+            return jsonify({'error': 'Permission already exists'}), 409
+
+        # Create new permission
+        new_permission = FilePermissions(
+            file_id=data['file_id'],
+            user_id=data['user_id'],
+            encryption_key=data['key_for_recipient'],
+            created_at=datetime.utcnow(),
+            updated_at=datetime.utcnow()
+        )
+        db.session.add(new_permission)
path: src/server/routes/permission_routes.py
user: login: jeancarson
body: For now db is not defined bc we arent actually connected. I may have to make small changes to these files on this but I think it won't fully work until the db connection is established elsewhere anyways, so for now I just assumed that we had it in order to build the rest
created_at: 2025-05-22T09:40:42Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: jeancarson
body: Sorry - I looked at this again in the whole file. `filename` is defined above and is the user_id concatenated with the new santitized filename. We could change this if we make a server file structure (eg a folder for each user) but the intention was to group a users files together
created_at: 2025-05-22T10:18:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: so its not the file name to be stored?

created_at: 2025-05-22T11:31:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,216 @@
+import base64
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from werkzeug.utils import secure_filename
+import os
+from datetime import datetime, UTC
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    - size: File size in bytes
+    - format: File format/extension
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.now(UTC)
+        )
+        db.session.add(new_file)
+        db.session.flush() #so new file record is generated before we reference it below in metadata.
+        
+        # Create metadata entry using data from request
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=request.metadata['size'],
+            format=request.metadata['format'],
+            last_updated_at=datetime.now(UTC)
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+        db.session.close()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        db.session.close()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of file metadata (id, name, size, etc.)
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'format': file.metadata.format if file.metadata else None,
+            'uploaded_at': file.uploaded_at.isoformat() if file.uploaded_at else None,
+            'is_owner': True
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_file_ids = [permission.file_id for permission in shared_permissions]
+        shared_files = Files.query.filter(Files.id.in_(shared_file_ids)).all()
+        shared_files_map = {file.id: file for file in shared_files}
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = shared_files_map.get(permission.file_id)
+            if file:  # File might have been deleted
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'format': file.metadata.format if file.metadata else None,
+                    'uploaded_at': file.uploaded_at.isoformat() if file.uploaded_at else None,
+                    'is_owner': False
+                })
+
+        db.session.close()
+        return jsonify({
+            'owned_files': owned_files_data,
+            'shared_files': shared_files_data
+        })
+
+    except Exception as e:
+        db.session.close()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/<file_id>', methods=['GET'])
+def get_file(file_id):
+    """
+    Get a specific file by ID
+    Expects:
+    - file_id: The ID of the file
+    Returns:
+    - encrypted_file: The encrypted file data
+    - encrypted_keys: The encrypted keys for sharing
+    """
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Find the file
+        file = Files.query.get(file_id)
+        if not file:
+            db.session.close()
+            return jsonify({'error': 'File not found'}), 404
+
+        # Check if user has access
+        if file.owner_id != current_user.id:
+            # Check if file is shared with user
+            permission = FilePermissions.query.filter_by(
+                file_id=file_id,
+                user_id=current_user.id
+            ).first()
+            if not permission:
+                db.session.close()
+                return jsonify({'error': 'Not authorized to access this file'}), 403
+
+        # Read the encrypted file
+        try:
+            with open(file.path, 'rb') as f:
+                encrypted_file = f.read()
+        except Exception as e:
+            db.session.close()
+            return jsonify({'error': 'Error reading file'}), 500
+
+        response_data = {
+            'encrypted_file': base64.b64encode(encrypted_file).decode('utf-8')
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: why are we encoding and decoding?

created_at: 2025-05-22T11:33:19Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: Kabidoye-17
body: and im not seeing where the metadata is being uploaded with the file, maybe the comments blocking
created_at: 2025-05-22T11:34:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,216 @@
+import base64
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+from werkzeug.utils import secure_filename
+import os
+from datetime import datetime, UTC
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    - size: File size in bytes
+    - format: File format/extension
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
+        new_file = Files(
+            owner_id=current_user.id,
+            name=file.filename,
+            path=file_path,
+            uploaded_at=datetime.now(UTC)
+        )
+        db.session.add(new_file)
+        db.session.flush() #so new file record is generated before we reference it below in metadata.
+        
+        # Create metadata entry using data from request
+        file_metadata = FileMetadata(
+            file_id=new_file.id,
+            size=request.metadata['size'],
+            format=request.metadata['format'],
+            last_updated_at=datetime.now(UTC)
+        )
+        db.session.add(file_metadata)
+        
+        db.session.commit()
+        db.session.close()
+
+        return jsonify({
+            'message': 'File uploaded successfully',
+            'file_id': new_file.id
+        }), 201
+
+    except Exception as e:
+        # Clean up file if database operation fails
+        if 'file_path' in locals():
+            try:
+                os.remove(file_path)
+            except:
+                pass
+        db.session.rollback()
+        db.session.close()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/', methods=['GET'])
+def list_files():
+    """
+    List all files for the authenticated user, both in the file database and the filePermission database
+    Returns:
+    - files: A list of file metadata (id, name, size, etc.)
+    """
+    try:
+        current_user = get_current_user()
+        
+        # Get files owned by the user
+        owned_files = Files.query.filter_by(owner_id=current_user.id).all()
+        owned_files_data = [{
+            'id': file.id,
+            'filename': file.name,
+            'file_size': file.metadata.size if file.metadata else None,
+            'format': file.metadata.format if file.metadata else None,
+            'uploaded_at': file.uploaded_at.isoformat() if file.uploaded_at else None,
+            'is_owner': True
+        } for file in owned_files]
+
+        # Get files shared with the user
+        shared_permissions = FilePermissions.query.filter_by(user_id=current_user.id).all()
+        shared_file_ids = [permission.file_id for permission in shared_permissions]
+        shared_files = Files.query.filter(Files.id.in_(shared_file_ids)).all()
+        shared_files_map = {file.id: file for file in shared_files}
+        shared_files_data = []
+        for permission in shared_permissions:
+            file = shared_files_map.get(permission.file_id)
+            if file:  # File might have been deleted
+                shared_files_data.append({
+                    'id': file.id,
+                    'filename': file.name,
+                    'file_size': file.metadata.size if file.metadata else None,
+                    'format': file.metadata.format if file.metadata else None,
+                    'uploaded_at': file.uploaded_at.isoformat() if file.uploaded_at else None,
+                    'is_owner': False
+                })
+
+        db.session.close()
+        return jsonify({
+            'owned_files': owned_files_data,
+            'shared_files': shared_files_data
+        })
+
+    except Exception as e:
+        db.session.close()
+        return jsonify({'error': str(e)}), 500
+
+@files_bp.route('/<file_id>', methods=['GET'])
+def get_file(file_id):
+    """
+    Get a specific file by ID
+    Expects:
+    - file_id: The ID of the file
+    Returns:
+    - encrypted_file: The encrypted file data
+    - encrypted_keys: The encrypted keys for sharing
+    """
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Find the file
+        file = Files.query.get(file_id)
+        if not file:
+            db.session.close()
+            return jsonify({'error': 'File not found'}), 404
+
+        # Check if user has access
+        if file.owner_id != current_user.id:
+            # Check if file is shared with user
+            permission = FilePermissions.query.filter_by(
+                file_id=file_id,
+                user_id=current_user.id
+            ).first()
+            if not permission:
+                db.session.close()
+                return jsonify({'error': 'Not authorized to access this file'}), 403
+
+        # Read the encrypted file
+        try:
+            with open(file.path, 'rb') as f:
+                encrypted_file = f.read()
+        except Exception as e:
+            db.session.close()
+            return jsonify({'error': 'Error reading file'}), 500
+
+        response_data = {
+            'encrypted_file': base64.b64encode(encrypted_file).decode('utf-8')
path: src/server/routes/file_routes.py
user: login: jeancarson
body: Apparently if we serialise a binary file it can cause issues and get corrupted

created_at: 2025-05-22T11:39:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,197 @@
+from flask import Blueprint, jsonify, request
+from ..models.tables import Files, FilePermissions, Users, FileMetadata  
+from ..utils.auth import get_current_user  # Assuming we have this utility for now. Spoiler, we don't
+import os
+from datetime import datetime
+
+files_bp = Blueprint('files', __name__, url_prefix='/api/files')
+
+@files_bp.route('/upload', methods=['POST'])
+def upload_file():
+    """
+    Upload a new file
+    Expects:
+    - encrypted_file: The encrypted file data
+    """
+    if 'encrypted_file' not in request.files:
+        return jsonify({'error': 'No file provided'}), 400
+
+    try:
+        # Get the current user from the request (assuming we have authentication middleware)
+        current_user = get_current_user()
+        
+        # Save the file to disk (assuming we have a configured upload directory)
+        file = request.files['encrypted_file']
+        sanitized_filename = secure_filename(file.filename)
+        filename = f"{current_user.id}_{sanitized_filename}"  # Prefix with user ID
+        file_path = os.path.join('uploads', filename)  # Assuming we have an 'uploads' directory
+        file.save(file_path)
+
+        # Create database entry
path: src/server/routes/file_routes.py
user: login: jeancarson
body: Alright I think I have this now
In the database, we store the actual original filename, but the filepath is using a santitised version. This removes potentially dangerous characters such as ../ or \ which could be used in an attack like path traversal attacks (where an attacker tries to access files outside the intended directory) or command injection attacks.
File Metadata is added to the metadata table, a couple lines below, but please do have a look at the whole file to verify this is what you mean!!

created_at: 2025-05-22T12:22:36Z
author_association: COLLABORATOR

PR comments:
user: login: ElliceNels
created_at: 2025-05-22T09:40:49Z
body: Is it okay if we leave this here until the db stuff comes through so we can test it, making sure all code on main works?
author_association: OWNER
user: login: jeancarson
created_at: 2025-05-22T09:41:35Z
body: > Is it okay if we leave this here until the db stuff comes through so we can test it, making sure all code on main works?

Ok with me. I'll make some changes and ask if we can resolve conversations later. 
author_association: COLLABORATOR
user: login: ElliceNels
created_at: 2025-05-22T16:22:28Z
body: i think all your closes disappeared LOL
author_association: OWNER