feat: implement security, error resilience, and query optimization proposals
Security Validation (enhance-security-validation): - JWT secret validation with entropy checking and pattern detection - CSRF protection middleware with token generation/validation - Frontend CSRF token auto-injection for DELETE/PUT/PATCH requests - MIME type validation with magic bytes detection for file uploads Error Resilience (add-error-resilience): - React ErrorBoundary component with fallback UI and retry functionality - ErrorBoundaryWithI18n wrapper for internationalization support - Page-level and section-level error boundaries in App.tsx Query Performance (optimize-query-performance): - Query monitoring utility with threshold warnings - N+1 query fixes using joinedload/selectinload - Optimized project members, tasks, and subtasks endpoints Bug Fixes: - WebSocket session management (P0): Return primitives instead of ORM objects - LIKE query injection (P1): Escape special characters in search queries Tests: 543 backend tests, 56 frontend tests passing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import uuid
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import List, Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query, Request
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import Session, joinedload, selectinload
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.redis_pubsub import publish_task_event
|
||||
@@ -110,6 +110,9 @@ async def list_tasks(
|
||||
|
||||
The due_after and due_before parameters are useful for calendar view
|
||||
to fetch tasks within a specific date range.
|
||||
|
||||
Optimized to avoid N+1 queries by using selectinload for task relationships.
|
||||
This batch loads assignees, statuses, creators and subtasks efficiently.
|
||||
"""
|
||||
project = db.query(Project).filter(Project.id == project_id).first()
|
||||
|
||||
@@ -125,7 +128,15 @@ async def list_tasks(
|
||||
detail="Access denied",
|
||||
)
|
||||
|
||||
query = db.query(Task).filter(Task.project_id == project_id)
|
||||
# Use selectinload to eagerly load task relationships
|
||||
# This avoids N+1 queries when accessing task.assignee, task.status, etc.
|
||||
query = db.query(Task).options(
|
||||
selectinload(Task.assignee),
|
||||
selectinload(Task.status),
|
||||
selectinload(Task.creator),
|
||||
selectinload(Task.subtasks),
|
||||
selectinload(Task.custom_values),
|
||||
).filter(Task.project_id == project_id)
|
||||
|
||||
# Filter deleted tasks (only admin can include deleted)
|
||||
if include_deleted and current_user.is_system_admin:
|
||||
@@ -1112,6 +1123,8 @@ async def list_subtasks(
|
||||
):
|
||||
"""
|
||||
List subtasks of a task.
|
||||
|
||||
Optimized to avoid N+1 queries by using selectinload for task relationships.
|
||||
"""
|
||||
task = db.query(Task).filter(Task.id == task_id).first()
|
||||
|
||||
@@ -1127,7 +1140,13 @@ async def list_subtasks(
|
||||
detail="Access denied",
|
||||
)
|
||||
|
||||
query = db.query(Task).filter(Task.parent_task_id == task_id)
|
||||
# Use selectinload to eagerly load subtask relationships
|
||||
query = db.query(Task).options(
|
||||
selectinload(Task.assignee),
|
||||
selectinload(Task.status),
|
||||
selectinload(Task.creator),
|
||||
selectinload(Task.subtasks),
|
||||
).filter(Task.parent_task_id == task_id)
|
||||
|
||||
# Filter deleted subtasks (only admin can include deleted)
|
||||
if not (include_deleted and current_user.is_system_admin):
|
||||
|
||||
Reference in New Issue
Block a user