NeahNew/PROJECT_DEEP_ANALYSIS.md
2026-01-08 16:17:13 +01:00

754 lines
20 KiB
Markdown

# Neah Project - Deep Technical Analysis
## Executive Summary
This document provides a comprehensive analysis of the Neah project architecture, focusing on:
- Update Services & Refresh Management
- Widgets Architecture
- Notifications System
- Authentication & Token Refresh
- Performance & Memory Management
- API Routes Tracing
---
## 1. Update Services & Refresh Management
### 1.1 Unified Refresh Manager (`lib/services/refresh-manager.ts`)
**Architecture:**
- **Singleton Pattern**: Single instance manages all refresh operations
- **Resource-Based**: Each refreshable resource has its own configuration
- **Deduplication**: Prevents duplicate refresh requests
- **Interval Management**: Centralized interval control
**Refreshable Resources:**
```typescript
type RefreshableResource =
| 'notifications'
| 'notifications-count'
| 'calendar'
| 'news'
| 'email'
| 'parole'
| 'duties'
| 'navbar-time';
```
**Key Features:**
1. **Request Deduplication**
- Minimum 1 second between refreshes for same resource
- Tracks pending requests to prevent duplicates
- Uses `pendingRequests` Map with promise tracking
2. **Interval Management**
- Each resource can have different refresh intervals
- Automatic cleanup on unregister
- Pause/Resume functionality for all resources
3. **Error Handling**
- Errors don't update `lastRefresh` timestamp (allows retry)
- Comprehensive logging for debugging
- Graceful degradation on failures
**Memory Impact:**
- **Low**: Uses Maps for efficient lookups
- **Cleanup**: Proper cleanup on component unmount
- **Potential Issue**: If components don't unregister, intervals persist
**Performance Considerations:**
- ✅ Deduplication prevents unnecessary API calls
- ✅ Minimum 1s throttle prevents excessive refreshes
- ⚠️ Multiple resources = multiple intervals (but necessary)
- ⚠️ No priority-based scheduling (all resources treated equally)
### 1.2 Unified Refresh Hook (`hooks/use-unified-refresh.ts`)
**Purpose:** React hook wrapper for RefreshManager
**Key Features:**
- Automatic registration/unregistration on mount/unmount
- Session-aware (only active when authenticated)
- Callback ref pattern to avoid stale closures
- Manual refresh trigger with force option
**Usage Pattern:**
```typescript
const { refresh, isActive } = useUnifiedRefresh({
resource: 'calendar',
interval: 300000, // 5 minutes
enabled: status === 'authenticated',
onRefresh: fetchEvents,
priority: 'low',
});
```
**Memory Leak Prevention:**
- ✅ Cleanup in useEffect return
- ✅ isMountedRef prevents state updates after unmount
- ✅ Automatic unregister on unmount
---
## 2. Widgets Architecture
### 2.1 Widget Components Overview
**Main Dashboard Widgets** (`app/page.tsx`):
1. **QuoteCard** - Inspirational quotes
2. **Calendar** - Upcoming events (7 events)
3. **News** - News articles (100 limit)
4. **Duties** - Leantime tasks (7 tasks)
5. **Email** - Email preview (5 emails)
6. **Parole** - RocketChat messages
### 2.2 Widget Refresh Patterns
**Current Implementation Issues:**
1. **Calendar Widget** (`components/calendar.tsx`)
- ❌ No unified refresh integration
- ❌ Manual refresh only via button
- ❌ Fetches on mount only
- ⚠️ Uses `?refresh=true` parameter (bypasses cache)
2. **News Widget** (`components/news.tsx`)
- ❌ No unified refresh integration
- ✅ Manual refresh button
- ✅ Fetches on authentication
- ⚠️ Uses `?refresh=true` parameter
3. **Email Widget** (`components/email.tsx`)
- ❌ No unified refresh integration
- ✅ Manual refresh button
- ⚠️ Fetches on mount only
- ⚠️ Uses `?refresh=true` parameter
4. **Parole Widget** (`components/parole.tsx`)
- ❌ No unified refresh integration
- ⚠️ **Custom polling**: `setInterval(() => fetchMessages(), 30000)` (30s)
- ⚠️ **Memory Leak Risk**: Interval not cleared if component unmounts during fetch
- ✅ Manual refresh button
5. **Duties Widget** (`components/flow.tsx`)
- ❌ No unified refresh integration
- ❌ Fetches on mount only
- ⚠️ Uses `?refresh=true` parameter
### 2.3 Widget Memory & Performance Issues
**Critical Issues:**
1. **Multiple Polling Mechanisms**
- Parole widget uses `setInterval` (30s)
- No coordination with RefreshManager
- Risk of memory leaks if cleanup fails
2. **Cache Bypassing**
- Most widgets use `?refresh=true`
- Bypasses Redis cache
- Increases server load
3. **No Unified Refresh**
- Widgets don't use `useUnifiedRefresh` hook
- Inconsistent refresh patterns
- Hard to manage globally
4. **State Management**
- Each widget manages its own state
- No shared state/cache
- Potential duplicate API calls
**Recommendations:**
- ✅ Migrate all widgets to use `useUnifiedRefresh`
- ✅ Remove custom `setInterval` implementations
- ✅ Use cache-first strategy (remove `?refresh=true` by default)
- ✅ Implement widget-level error boundaries
---
## 3. Notifications System
### 3.1 Architecture Overview
**Service Pattern:** Singleton with adapter pattern
**Location:** `lib/services/notifications/notification-service.ts`
**Adapters:**
- `LeantimeAdapter` (implemented)
- NextcloudAdapter (planned)
- GiteaAdapter (planned)
- DolibarrAdapter (planned)
- MoodleAdapter (planned)
### 3.2 Caching Strategy
**Redis Cache Keys:**
```typescript
NOTIFICATION_COUNT_CACHE_KEY = `notifications:count:${userId}`
NOTIFICATIONS_LIST_CACHE_KEY = `notifications:list:${userId}:${page}:${limit}`
```
**Cache TTL:**
- Count cache: 30 seconds
- List cache: 30 seconds
- Refresh lock: 30 seconds
**Cache Invalidation:**
- On `markAsRead`: Invalidates all user caches
- Uses Redis SCAN for pattern matching
- Prevents blocking operations
### 3.3 Refresh Management
**Integration with RefreshManager:**
- ✅ Uses unified refresh system
- ✅ Registered as 'notifications' and 'notifications-count'
- ✅ 30-second refresh interval (aligned with cache TTL)
**Hook Usage** (`hooks/use-notifications.ts`):
- Request deduplication (2-second window)
- Automatic refresh on mount
- Manual refresh capability
- Error handling with retry
### 3.4 Performance Characteristics
**Strengths:**
- ✅ Redis caching reduces database load
- ✅ Adapter pattern allows easy extension
- ✅ Parallel fetching from multiple adapters
- ✅ Request deduplication prevents duplicate calls
**Potential Issues:**
- ⚠️ SCAN operations can be slow with many keys
- ⚠️ No pagination limits on adapter results
- ⚠️ All adapters fetched in parallel (could be optimized)
**Memory Impact:**
- **Low**: Cached data in Redis, not memory
- **Medium**: Notification objects in React state
- **Low**: Adapter instances are singletons
---
## 4. Authentication & Token Refresh
### 4.1 Keycloak Integration
**Provider:** NextAuth with KeycloakProvider
**Location:** `app/api/auth/options.ts`
### 4.2 Token Refresh Flow
**JWT Callback Logic:**
1. **Initial Sign-In:**
- Stores access token, refresh token, ID token
- Extracts roles from access token
- Sets expiration timestamp
2. **Subsequent Requests:**
- Checks if token is expired
- If expired, calls `refreshAccessToken()`
- Updates token with new values
**Refresh Function** (`refreshAccessToken`):
```typescript
async function refreshAccessToken(token: ExtendedJWT) {
// Calls Keycloak token endpoint
// Handles various error scenarios:
// - SessionNotActive (user logged out)
// - RefreshTokenExpired (inactivity)
// - InvalidGrant (session invalidated)
}
```
**Error Handling:**
- ✅ Detects session invalidation
- ✅ Handles refresh token expiration
- ✅ Clears tokens on critical errors
- ✅ Returns null session to trigger re-auth
### 4.3 Session Management
**Session Configuration:**
- Strategy: JWT (stateless)
- Max Age: 4 hours (14,400 seconds)
- Automatic refresh on activity
**Cookie Configuration:**
- HttpOnly: true
- SameSite: 'lax'
- Secure: Based on NEXTAUTH_URL
### 4.4 Email OAuth Token Refresh
**Service:** `lib/services/token-refresh.ts`
**Purpose:** Refresh Microsoft OAuth tokens for email access
**Flow:**
1. Check Redis cache for credentials
2. If cache miss, check Prisma database
3. Validate token expiration (5-minute buffer)
4. Refresh if needed via Microsoft OAuth
5. Update both Redis and Prisma
**Dual Storage:**
- **Redis**: Fast access, 24-hour TTL
- **Prisma**: Persistent storage, survives Redis restarts
**Memory Impact:**
- **Low**: Credentials stored in Redis/DB, not memory
- **Medium**: Token refresh operations are async
- **Low**: No memory leaks (proper cleanup)
### 4.5 Performance Considerations
**Token Refresh Frequency:**
- Keycloak: On every request if expired
- Email OAuth: Only when expired (5-min buffer)
**Optimization Opportunities:**
- ⚠️ Token refresh happens synchronously in JWT callback
- ⚠️ Could implement background refresh
- ✅ Caching reduces refresh frequency
---
## 5. Performance & Memory Management
### 5.1 Next.js Configuration
**Build Configuration** (`next.config.mjs`):
```javascript
experimental: {
webpackBuildWorker: true,
parallelServerBuildTraces: true,
parallelServerCompiles: true,
}
```
**Memory Impact:**
- ✅ Parallel builds reduce build time
- ⚠️ Multiple workers increase memory during build
- ✅ Production builds are optimized
### 5.2 Redis Connection Management
**Singleton Pattern** (`lib/redis.ts`):
- Single Redis client instance
- Connection pooling
- Automatic reconnection with retry strategy
**Memory Impact:**
- **Low**: Single connection per process
- **Medium**: Connection pool (if configured)
- **Low**: Proper cleanup on disconnect
**Connection Strategy:**
- Max reconnect attempts: 5
- Exponential backoff
- Connection timeout: 10 seconds
- Keep-alive: 10 seconds
### 5.3 Caching Strategy
**Redis Cache TTLs:**
```typescript
CREDENTIALS: 24 hours
SESSION: 4 hours
EMAIL_LIST: 5 minutes
EMAIL_CONTENT: 15 minutes
CALENDAR: 10 minutes
NEWS: 15 minutes
TASKS: 10 minutes
MESSAGES: 2 minutes
NOTIFICATIONS: 30 seconds
```
**Memory Impact:**
- **Low**: Data in Redis, not application memory
- **Medium**: Large cache can consume Redis memory
- **Low**: TTL ensures automatic cleanup
### 5.4 Component Memory Management
**Potential Memory Leaks:**
1. **Parole Widget** (`components/parole.tsx`):
```typescript
// ⚠️ RISK: Interval might not clear if component unmounts during fetch
useEffect(() => {
if (status === 'authenticated') {
fetchMessages();
const interval = setInterval(() => fetchMessages(), 30000);
return () => clearInterval(interval); // ✅ Good, but...
}
}, [status]);
```
**Issue**: If `fetchMessages()` is async and component unmounts, state updates may occur
2. **Widget State:**
- Each widget maintains its own state
- No cleanup on unmount for pending requests
- Potential memory leaks with large data arrays
3. **Event Listeners:**
- No evidence of unregistered event listeners
- ✅ React handles most cleanup automatically
### 5.5 API Route Performance
**Common Patterns:**
1. **Session Validation:**
```typescript
const session = await getServerSession(authOptions);
```
- Called on every request
- JWT validation overhead
- Could be optimized with middleware
2. **Database Queries:**
- Prisma ORM adds overhead
- No query optimization visible
- Connection pooling handled by Prisma
3. **Redis Operations:**
- Most routes check cache first
- SCAN operations for pattern matching
- Could be optimized with better key patterns
### 5.6 Memory Optimization Recommendations
**High Priority:**
1. ✅ Fix Parole widget interval cleanup
2. ✅ Migrate widgets to unified refresh
3. ✅ Implement request cancellation for unmounted components
4. ✅ Add error boundaries to prevent memory leaks
**Medium Priority:**
1. ⚠️ Implement API route middleware for auth
2. ⚠️ Optimize Redis SCAN operations
3. ⚠️ Add request timeout handling
4. ⚠️ Implement connection pooling for external APIs
**Low Priority:**
1. ⚠️ Consider React Query for state management
2. ⚠️ Implement virtual scrolling for large lists
3. ⚠️ Add memory profiling tools
---
## 6. API Routes Tracing
### 6.1 Logging Infrastructure
**Logger** (`lib/logger.ts`):
- Environment-aware (silent in production for debug/info)
- Always logs errors
- Simple console-based logging
**Limitations:**
- ❌ No structured logging (JSON)
- ❌ No log levels in production
- ❌ No centralized log aggregation
- ❌ No request tracing IDs
### 6.2 Current Logging Patterns
**API Routes:**
- 343 `console.log/error/warn` calls across 68 files
- Inconsistent logging patterns
- Some routes have detailed logging, others minimal
**Examples:**
1. **Good Logging** (`app/api/missions/mission-created/route.ts`):
```typescript
logger.debug('Mission Created Webhook Received');
logger.debug('Received mission-created data', { ... });
```
2. **Inconsistent Logging** (`app/api/courrier/route.ts`):
```typescript
console.log(`[API] Received request with: ...`);
// Mix of console.log and logger
```
### 6.3 API Route Categories
**Authentication Routes:**
- `/api/auth/[...nextauth]` - NextAuth handler
- `/api/auth/refresh-keycloak-session` - Session refresh
- `/api/auth/debug-keycloak` - Debug endpoint
**Email Routes (Courrier):**
- `/api/courrier` - Email list
- `/api/courrier/emails` - Email list (alternative)
- `/api/courrier/[id]` - Single email
- `/api/courrier/refresh` - Token refresh
- `/api/courrier/session` - IMAP session
- `/api/courrier/account` - Account management
**Calendar Routes:**
- `/api/calendars` - Calendar list
- `/api/calendars/[id]` - Single calendar
- `/api/calendars/[id]/events` - Calendar events
- `/api/events` - Event CRUD
**Notification Routes:**
- `/api/notifications` - Notification list
- `/api/notifications/count` - Notification count
- `/api/notifications/[id]/read` - Mark as read
- `/api/notifications/read-all` - Mark all as read
**Mission Routes:**
- `/api/missions` - Mission list
- `/api/missions/[missionId]` - Single mission
- `/api/missions/upload` - File upload
- `/api/missions/mission-created` - Webhook handler
### 6.4 Tracing Recommendations
**Immediate Improvements:**
1. **Request ID Tracking:**
```typescript
// Add to middleware or API route wrapper
const requestId = crypto.randomUUID();
logger.info('Request started', { requestId, path, method });
```
2. **Structured Logging:**
```typescript
logger.info('API Request', {
requestId,
method,
path,
userId,
duration: Date.now() - startTime,
});
```
3. **Error Tracking:**
```typescript
logger.error('API Error', {
requestId,
error: error.message,
stack: error.stack,
path,
userId,
});
```
4. **Performance Monitoring:**
```typescript
const startTime = Date.now();
// ... route logic
logger.debug('API Response', {
requestId,
duration: Date.now() - startTime,
statusCode,
});
```
**Advanced Tracing:**
1. **OpenTelemetry Integration:**
- Distributed tracing
- Performance metrics
- Error tracking
2. **APM Tools:**
- New Relic
- Datadog
- Sentry
3. **Custom Middleware:**
```typescript
// app/api/middleware.ts
export function withTracing(handler: Function) {
return async (req: Request, res: Response) => {
const requestId = crypto.randomUUID();
const startTime = Date.now();
try {
const result = await handler(req, res);
logger.info('Request completed', {
requestId,
duration: Date.now() - startTime,
});
return result;
} catch (error) {
logger.error('Request failed', {
requestId,
error,
duration: Date.now() - startTime,
});
throw error;
}
};
}
```
### 6.5 API Route Performance Metrics
**Current State:**
- ❌ No performance metrics collected
- ❌ No request duration tracking
- ❌ No error rate monitoring
- ❌ No cache hit/miss tracking
**Recommended Metrics:**
1. Request duration (p50, p95, p99)
2. Error rate by route
3. Cache hit/miss ratio
4. Database query count
5. Redis operation count
6. External API call duration
---
## 7. Critical Issues & Recommendations
### 7.1 Critical Issues
1. **Memory Leak Risk - Parole Widget**
- Custom `setInterval` without proper cleanup
- **Fix**: Migrate to `useUnifiedRefresh`
2. **Inconsistent Refresh Patterns**
- Widgets don't use unified refresh system
- **Fix**: Migrate all widgets to `useUnifiedRefresh`
3. **Cache Bypassing**
- Widgets use `?refresh=true` by default
- **Fix**: Use cache-first strategy
4. **No Request Tracing**
- Difficult to debug production issues
- **Fix**: Implement request ID tracking
5. **No Performance Monitoring**
- No visibility into slow routes
- **Fix**: Add performance metrics
### 7.2 High Priority Recommendations
1. ✅ Migrate all widgets to unified refresh system
2. ✅ Fix Parole widget interval cleanup
3. ✅ Implement request ID tracking
4. ✅ Add performance metrics
5. ✅ Standardize logging patterns
### 7.3 Medium Priority Recommendations
1. ⚠️ Implement API route middleware
2. ⚠️ Optimize Redis SCAN operations
3. ⚠️ Add error boundaries
4. ⚠️ Implement request cancellation
5. ⚠️ Add structured logging
### 7.4 Low Priority Recommendations
1. ⚠️ Consider React Query
2. ⚠️ Implement virtual scrolling
3. ⚠️ Add memory profiling
4. ⚠️ Consider OpenTelemetry
5. ⚠️ Add APM tooling
---
## 8. Architecture Strengths
### 8.1 Well-Designed Components
1. **Unified Refresh Manager**
- Excellent abstraction
- Proper deduplication
- Clean API
2. **Notification Service**
- Adapter pattern allows extension
- Good caching strategy
- Proper error handling
3. **Redis Integration**
- Comprehensive caching
- Proper TTL management
- Good key naming conventions
4. **Token Refresh**
- Dual storage (Redis + Prisma)
- Proper error handling
- Automatic refresh
### 8.2 Code Quality
- ✅ TypeScript throughout
- ✅ Consistent component structure
- ✅ Proper error handling in most places
- ✅ Good separation of concerns
---
## 9. Conclusion
The Neah project demonstrates a well-architected Next.js application with several sophisticated systems:
**Strengths:**
- Unified refresh management system
- Comprehensive caching strategy
- Robust authentication flow
- Extensible notification system
**Areas for Improvement:**
- Widget refresh consistency
- Memory leak prevention
- API route tracing
- Performance monitoring
**Overall Assessment:**
The codebase is production-ready but would benefit from the recommended improvements, particularly around widget refresh management and observability.
---
## Appendix: File Reference Map
### Core Services
- `lib/services/refresh-manager.ts` - Unified refresh management
- `lib/services/notifications/notification-service.ts` - Notification system
- `lib/services/token-refresh.ts` - Email OAuth token refresh
- `lib/redis.ts` - Redis caching utilities
- `lib/logger.ts` - Logging utility
### Hooks
- `hooks/use-unified-refresh.ts` - Unified refresh hook
- `hooks/use-notifications.ts` - Notification hook
### Widgets
- `components/calendar.tsx` - Calendar widget
- `components/news.tsx` - News widget
- `components/email.tsx` - Email widget
- `components/parole.tsx` - Messages widget
- `components/flow.tsx` - Tasks widget
### API Routes
- `app/api/auth/options.ts` - NextAuth configuration
- `app/api/notifications/` - Notification endpoints
- `app/api/courrier/` - Email endpoints
- `app/api/calendars/` - Calendar endpoints
---
*Document generated: 2024*
*Last updated: Analysis session*