widget cache

This commit is contained in:
alma 2025-05-03 15:04:27 +02:00
parent 5f240717f8
commit 4a9be647ca
10 changed files with 318 additions and 15 deletions

View File

@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { prisma } from "@/lib/prisma";
import { getCachedCalendarData, cacheCalendarData } from "@/lib/redis";
/**
* Handles the GET request to retrieve calendars for the authenticated user.
@ -25,6 +26,21 @@ export async function GET(req: NextRequest) {
}
try {
// Check for force refresh parameter
const url = new URL(req.url);
const forceRefresh = url.searchParams.get('refresh') === 'true';
// Try to get data from cache if not forcing refresh
if (!forceRefresh) {
const cachedData = await getCachedCalendarData(session.user.id);
if (cachedData) {
console.log(`Using cached calendar data for user ${session.user.id}`);
return NextResponse.json(cachedData);
}
}
// If no cache or forcing refresh, fetch from database
console.log(`Fetching calendar data from database for user ${session.user.id}`);
const calendars = await prisma.calendar.findMany({
where: {
userId: session.user.id,
@ -42,6 +58,10 @@ export async function GET(req: NextRequest) {
});
console.log("Fetched calendars with events:", calendars);
// Cache the results
await cacheCalendarData(session.user.id, calendars);
return NextResponse.json(calendars);
} catch (error) {
console.error("Erreur lors de la récupération des calendriers:", error);

View File

@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { getCachedTasksData, cacheTasksData } from "@/lib/redis";
interface Task {
id: string;
@ -94,6 +95,19 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Check for force refresh parameter
const url = new URL(request.url);
const forceRefresh = url.searchParams.get('refresh') === 'true';
// Try to get data from cache if not forcing refresh
if (!forceRefresh) {
const cachedTasks = await getCachedTasksData(session.user.id);
if (cachedTasks) {
console.log(`Using cached tasks data for user ${session.user.id}`);
return NextResponse.json(cachedTasks);
}
}
console.log('Fetching tasks for user:', session.user.email);
const userId = await getLeantimeUserId(session.user.email);
@ -195,6 +209,10 @@ export async function GET(request: NextRequest) {
}));
console.log(`Found ${tasks.length} tasks assigned to user ${userId}`);
// Cache the results
await cacheTasksData(session.user.id, tasks);
return NextResponse.json(tasks);
} catch (error) {
console.error('Error in tasks route:', error);

View File

@ -1,5 +1,6 @@
import { NextResponse } from 'next/server';
import { env } from '@/lib/env';
import { getCachedNewsData, cacheNewsData } from '@/lib/redis';
// Helper function to clean HTML content
function cleanHtmlContent(text: string): string {
@ -78,8 +79,21 @@ interface NewsItem {
url: string;
}
export async function GET() {
export async function GET(request: Request) {
try {
// Check if we should bypass cache
const url = new URL(request.url);
const forceRefresh = url.searchParams.get('refresh') === 'true';
// Try to get data from cache if not forcing refresh
if (!forceRefresh) {
const cachedNews = await getCachedNewsData();
if (cachedNews) {
console.log('Using cached news data');
return NextResponse.json(cachedNews);
}
}
console.log('Fetching news from FastAPI server...');
const response = await fetch(`${env.NEWS_API_URL}/news?limit=12`, {
@ -129,6 +143,9 @@ export async function GET() {
url: article.url
}));
// Cache the results
await cacheNewsData(formattedNews);
return NextResponse.json(formattedNews);
} catch (error) {
console.error('News API error:', error);

View File

@ -1,6 +1,7 @@
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { NextResponse } from "next/server";
import { getCachedMessagesData, cacheMessagesData } from "@/lib/redis";
// Helper function to get user token using admin credentials
async function getUserToken(baseUrl: string) {
@ -43,6 +44,19 @@ export async function GET(request: Request) {
return NextResponse.json({ messages: [] }, { status: 200 });
}
// Check for force refresh parameter
const url = new URL(request.url);
const forceRefresh = url.searchParams.get('refresh') === 'true';
// Try to get data from cache if not forcing refresh
if (!forceRefresh) {
const cachedMessages = await getCachedMessagesData(session.user.id);
if (cachedMessages) {
console.log(`Using cached messages data for user ${session.user.id}`);
return NextResponse.json(cachedMessages);
}
}
const baseUrl = process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL?.split('/channel')[0];
if (!baseUrl) {
console.error('Failed to get Rocket.Chat base URL');
@ -337,14 +351,15 @@ export async function GET(request: Request) {
})
.slice(0, 10);
return NextResponse.json({
messages: sortedMessages,
total: Object.keys(latestMessagePerRoom).length,
hasMore: Object.keys(latestMessagePerRoom).length > 10
}, { status: 200 });
const finalResponse = { messages: sortedMessages, total: Object.keys(latestMessagePerRoom).length, hasMore: Object.keys(latestMessagePerRoom).length > 10 };
// Cache the results
await cacheMessagesData(session.user.id, finalResponse);
return NextResponse.json(finalResponse);
} catch (error) {
console.error('Error in messages endpoint:', error);
return NextResponse.json({ messages: [], total: 0, hasMore: false }, { status: 200 });
console.error('Error fetching messages:', error);
return NextResponse.json({ error: 'Failed to fetch messages' }, { status: 500 });
}
}

View File

@ -25,7 +25,7 @@ export function Calendar() {
const fetchEvents = async () => {
setLoading(true);
try {
const response = await fetch('/api/calendars');
const response = await fetch('/api/calendars?refresh=true');
if (!response.ok) {
throw new Error('Failed to fetch events');
}

View File

@ -38,7 +38,7 @@ export function Email() {
setError(null);
try {
const response = await fetch('/api/courrier?folder=INBOX&page=1&perPage=5');
const response = await fetch('/api/courrier?folder=INBOX&page=1&perPage=5' + (isRefresh ? '&refresh=true' : ''));
if (!response.ok) {
throw new Error('Failed to fetch emails');
}

View File

@ -92,7 +92,7 @@ export function Duties() {
setLoading(true);
setError(null);
try {
const response = await fetch('/api/leantime/tasks');
const response = await fetch('/api/leantime/tasks?refresh=true');
if (!response.ok) {
throw new Error('Failed to fetch tasks');
}

View File

@ -30,7 +30,7 @@ export function News() {
if (!isRefresh) setLoading(true);
try {
const response = await fetch('/api/news');
const response = await fetch(isRefresh ? '/api/news?refresh=true' : '/api/news');
if (!response.ok) {
throw new Error('Failed to fetch news');
}

View File

@ -48,7 +48,7 @@ export function Parole() {
setRefreshing(true);
}
const response = await fetch('/api/rocket-chat/messages', {
const response = await fetch('/api/rocket-chat/messages' + (isRefresh ? '?refresh=true' : ''), {
cache: 'no-store',
next: { revalidate: 0 },
});

View File

@ -127,7 +127,12 @@ export const KEYS = {
EMAIL_LIST: (userId: string, accountId: string, folder: string, page: number, perPage: number) =>
`email:list:${userId}:${accountId}:${folder}:${page}:${perPage}`,
EMAIL_CONTENT: (userId: string, accountId: string, emailId: string) =>
`email:content:${userId}:${accountId}:${emailId}`
`email:content:${userId}:${accountId}:${emailId}`,
// New widget cache keys
CALENDAR: (userId: string) => `widget:calendar:${userId}`,
NEWS: () => `widget:news`, // Global news cache, not user-specific
TASKS: (userId: string) => `widget:tasks:${userId}`,
MESSAGES: (userId: string) => `widget:messages:${userId}`
};
// TTL constants in seconds
@ -135,7 +140,12 @@ export const TTL = {
CREDENTIALS: 60 * 60 * 24, // 24 hours
SESSION: 60 * 60 * 4, // 4 hours (increased from 30 minutes)
EMAIL_LIST: 60 * 5, // 5 minutes
EMAIL_CONTENT: 60 * 15 // 15 minutes
EMAIL_CONTENT: 60 * 15, // 15 minutes
// New widget cache TTLs
CALENDAR: 60 * 10, // 10 minutes for calendar events
NEWS: 60 * 15, // 15 minutes for news
TASKS: 60 * 10, // 10 minutes for tasks
MESSAGES: 60 * 2 // 2 minutes for messages (more frequent updates)
};
interface EmailCredentials {
@ -494,4 +504,227 @@ export async function getCachedEmailCredentials(
accountId: string
): Promise<EmailCredentials | null> {
return getEmailCredentials(userId, accountId);
}
/**
* Cache calendar data for a user
*/
export async function cacheCalendarData(
userId: string,
data: any
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.CALENDAR(userId);
try {
await redis.set(key, JSON.stringify(data), 'EX', TTL.CALENDAR);
console.log(`Calendar data cached for user ${userId}`);
} catch (error) {
console.error(`Error caching calendar data for user ${userId}:`, error);
}
}
/**
* Get cached calendar data for a user
*/
export async function getCachedCalendarData(
userId: string
): Promise<any | null> {
const redis = getRedisClient();
const key = KEYS.CALENDAR(userId);
try {
const cachedData = await redis.get(key);
if (!cachedData) {
return null;
}
return JSON.parse(cachedData);
} catch (error) {
console.error(`Error getting cached calendar data for user ${userId}:`, error);
return null;
}
}
/**
* Invalidate calendar cache for a user
*/
export async function invalidateCalendarCache(
userId: string
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.CALENDAR(userId);
try {
await redis.del(key);
console.log(`Calendar cache invalidated for user ${userId}`);
} catch (error) {
console.error(`Error invalidating calendar cache for user ${userId}:`, error);
}
}
/**
* Cache news data (global, not user-specific)
*/
export async function cacheNewsData(
data: any
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.NEWS();
try {
await redis.set(key, JSON.stringify(data), 'EX', TTL.NEWS);
console.log('News data cached successfully');
} catch (error) {
console.error('Error caching news data:', error);
}
}
/**
* Get cached news data
*/
export async function getCachedNewsData(): Promise<any | null> {
const redis = getRedisClient();
const key = KEYS.NEWS();
try {
const cachedData = await redis.get(key);
if (!cachedData) {
return null;
}
return JSON.parse(cachedData);
} catch (error) {
console.error('Error getting cached news data:', error);
return null;
}
}
/**
* Invalidate news cache
*/
export async function invalidateNewsCache(): Promise<void> {
const redis = getRedisClient();
const key = KEYS.NEWS();
try {
await redis.del(key);
console.log('News cache invalidated');
} catch (error) {
console.error('Error invalidating news cache:', error);
}
}
/**
* Cache tasks data for a user
*/
export async function cacheTasksData(
userId: string,
data: any
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.TASKS(userId);
try {
await redis.set(key, JSON.stringify(data), 'EX', TTL.TASKS);
console.log(`Tasks data cached for user ${userId}`);
} catch (error) {
console.error(`Error caching tasks data for user ${userId}:`, error);
}
}
/**
* Get cached tasks data for a user
*/
export async function getCachedTasksData(
userId: string
): Promise<any | null> {
const redis = getRedisClient();
const key = KEYS.TASKS(userId);
try {
const cachedData = await redis.get(key);
if (!cachedData) {
return null;
}
return JSON.parse(cachedData);
} catch (error) {
console.error(`Error getting cached tasks data for user ${userId}:`, error);
return null;
}
}
/**
* Invalidate tasks cache for a user
*/
export async function invalidateTasksCache(
userId: string
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.TASKS(userId);
try {
await redis.del(key);
console.log(`Tasks cache invalidated for user ${userId}`);
} catch (error) {
console.error(`Error invalidating tasks cache for user ${userId}:`, error);
}
}
/**
* Cache messages data for a user
*/
export async function cacheMessagesData(
userId: string,
data: any
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.MESSAGES(userId);
try {
await redis.set(key, JSON.stringify(data), 'EX', TTL.MESSAGES);
console.log(`Messages data cached for user ${userId}`);
} catch (error) {
console.error(`Error caching messages data for user ${userId}:`, error);
}
}
/**
* Get cached messages data for a user
*/
export async function getCachedMessagesData(
userId: string
): Promise<any | null> {
const redis = getRedisClient();
const key = KEYS.MESSAGES(userId);
try {
const cachedData = await redis.get(key);
if (!cachedData) {
return null;
}
return JSON.parse(cachedData);
} catch (error) {
console.error(`Error getting cached messages data for user ${userId}:`, error);
return null;
}
}
/**
* Invalidate messages cache for a user
*/
export async function invalidateMessagesCache(
userId: string
): Promise<void> {
const redis = getRedisClient();
const key = KEYS.MESSAGES(userId);
try {
await redis.del(key);
console.log(`Messages cache invalidated for user ${userId}`);
} catch (error) {
console.error(`Error invalidating messages cache for user ${userId}:`, error);
}
}