/**
* Recommendations Server Actions
* Provides personalized post recommendations to users
*/
'use server';
import { auth } from '@/auth';
import { generateRecommendations } from '@/lib/recommendations/algorithm';
import { getPosts } from './forum';
import type { ForumPost, ApiResponse } from '@/types/forum';
// Simple in-memory cache (in production, use Redis)
const recommendationCache = new Map<
string,
{ posts: ForumPost[]; timestamp: number }
>();
const CACHE_TTL = 60 * 60 * 1000; // 1 hour in milliseconds
/**
* Get personalized post recommendations for the current user
* Falls back to trending posts for unauthenticated users
*/
export async function getRecommendedPosts(
limit: number = 5
): Promise<ApiResponse<ForumPost[]>> {
try {
const session = await auth();
// Unauthenticated users get trending posts
if (!session?.user?.id) {
const trendingResult = await getPosts({ sort: 'trending', limit });
return {
success: true,
data: trendingResult.success ? trendingResult.data?.data || [] : [],
};
}
const userId = session.user.id;
// Check cache first
const cached = recommendationCache.get(userId);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return {
success: true,
data: cached.posts.slice(0, limit),
};
}
// Generate fresh recommendations
const recommendations = await generateRecommendations(userId, limit);
// Cache the results
recommendationCache.set(userId, {
posts: recommendations,
timestamp: Date.now(),
});
return {
success: true,
data: recommendations,
};
} catch (error) {
console.error('Error getting recommended posts:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to get recommendations',
};
}
}
/**
* Clear recommendation cache for a user (e.g., after voting)
*/
export async function invalidateRecommendationCache(userId?: string): Promise<void> {
try {
if (userId) {
recommendationCache.delete(userId);
} else {
const session = await auth();
if (session?.user?.id) {
recommendationCache.delete(session.user.id);
}
}
} catch (error) {
console.error('Error invalidating recommendation cache:', error);
}
}
/**
* Clear all recommendation caches (admin utility)
*/
export async function clearAllRecommendationCaches(): Promise<ApiResponse<void>> {
try {
recommendationCache.clear();
return { success: true };
} catch (error) {
console.error('Error clearing recommendation caches:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to clear caches',
};
}
}
/**
* Get cache statistics (admin utility)
*/
export async function getRecommendationCacheStats(): Promise<
ApiResponse<{
cacheSize: number;
oldestEntry: number;
newestEntry: number;
}>
> {
try {
let oldestEntry = Date.now();
let newestEntry = 0;
for (const [, value] of recommendationCache) {
if (value.timestamp < oldestEntry) oldestEntry = value.timestamp;
if (value.timestamp > newestEntry) newestEntry = value.timestamp;
}
return {
success: true,
data: {
cacheSize: recommendationCache.size,
oldestEntry,
newestEntry,
},
};
} catch (error) {
console.error('Error getting cache stats:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to get cache stats',
};
}
}