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:
beabigegg
2026-01-11 18:41:19 +08:00
parent 2cb591ef23
commit 679b89ae4c
41 changed files with 3673 additions and 153 deletions

View File

@@ -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):