Refactor Logging BIG
This commit is contained in:
parent
c1ab0d5f81
commit
9ee441773c
@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { authOptions } from "@/app/api/auth/options";
|
||||
import { getCachedTasksData, cacheTasksData } from "@/lib/redis";
|
||||
import { logger } from "@/lib/logger";
|
||||
|
||||
interface Task {
|
||||
id: string;
|
||||
@ -22,13 +23,15 @@ interface Task {
|
||||
async function getLeantimeUserId(email: string): Promise<number | null> {
|
||||
try {
|
||||
if (!process.env.LEANTIME_TOKEN) {
|
||||
console.error('LEANTIME_TOKEN is not set in environment variables');
|
||||
logger.error('[LEANTIME_TASKS] LEANTIME_TOKEN is not set in environment variables');
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('Fetching Leantime users for email:', email);
|
||||
console.log('API URL:', process.env.LEANTIME_API_URL);
|
||||
console.log('Token length:', process.env.LEANTIME_TOKEN.length);
|
||||
logger.debug('[LEANTIME_TASKS] Fetching Leantime users', {
|
||||
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
apiUrlPresent: !!process.env.LEANTIME_API_URL,
|
||||
tokenLength: process.env.LEANTIME_TOKEN.length,
|
||||
});
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
@ -46,13 +49,12 @@ async function getLeantimeUserId(email: string): Promise<number | null> {
|
||||
});
|
||||
|
||||
const responseText = await response.text();
|
||||
console.log('Raw Leantime response:', responseText);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch Leantime users:', {
|
||||
logger.error('[LEANTIME_TASKS] Failed to fetch Leantime users', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: Object.fromEntries(response.headers.entries())
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
@ -61,14 +63,14 @@ async function getLeantimeUserId(email: string): Promise<number | null> {
|
||||
try {
|
||||
data = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse Leantime response:', e);
|
||||
logger.error('[LEANTIME_TASKS] Failed to parse Leantime response', {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('Leantime users response:', data);
|
||||
|
||||
if (!data.result || !Array.isArray(data.result)) {
|
||||
console.error('Invalid response format from Leantime users API');
|
||||
logger.error('[LEANTIME_TASKS] Invalid response format from Leantime users API');
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -76,14 +78,21 @@ async function getLeantimeUserId(email: string): Promise<number | null> {
|
||||
const user = users.find((u: any) => u.username === email);
|
||||
|
||||
if (user) {
|
||||
console.log('Found Leantime user:', { id: user.id, username: user.username });
|
||||
logger.debug('[LEANTIME_TASKS] Found Leantime user', {
|
||||
id: user.id,
|
||||
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
});
|
||||
} else {
|
||||
console.log('No Leantime user found for username:', email);
|
||||
logger.warn('[LEANTIME_TASKS] No Leantime user found for username', {
|
||||
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
});
|
||||
}
|
||||
|
||||
return user ? user.id : null;
|
||||
} catch (error) {
|
||||
console.error('Error fetching Leantime user ID:', error);
|
||||
logger.error('[LEANTIME_TASKS] Error fetching Leantime user ID', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -103,20 +112,29 @@ export async function GET(request: NextRequest) {
|
||||
if (!forceRefresh) {
|
||||
const cachedTasks = await getCachedTasksData(session.user.id);
|
||||
if (cachedTasks) {
|
||||
console.log(`Using cached tasks data for user ${session.user.id}`);
|
||||
logger.debug('[LEANTIME_TASKS] Using cached tasks data', {
|
||||
userId: session.user.id,
|
||||
taskCount: Array.isArray(cachedTasks) ? cachedTasks.length : undefined,
|
||||
});
|
||||
return NextResponse.json(cachedTasks);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Fetching tasks for user:', session.user.email);
|
||||
logger.debug('[LEANTIME_TASKS] Fetching tasks for user', {
|
||||
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
});
|
||||
const userId = await getLeantimeUserId(session.user.email);
|
||||
|
||||
if (!userId) {
|
||||
console.error('User not found in Leantime');
|
||||
logger.error('[LEANTIME_TASKS] User not found in Leantime', {
|
||||
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
|
||||
});
|
||||
return NextResponse.json({ error: "User not found in Leantime" }, { status: 404 });
|
||||
}
|
||||
|
||||
console.log('Fetching tasks for Leantime user ID:', userId);
|
||||
logger.debug('[LEANTIME_TASKS] Fetching tasks for Leantime user ID', {
|
||||
userId,
|
||||
});
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': process.env.LEANTIME_TOKEN!
|
||||
@ -137,12 +155,14 @@ export async function GET(request: NextRequest) {
|
||||
});
|
||||
|
||||
const responseText = await response.text();
|
||||
console.log('Tasks API response status:', response.status);
|
||||
logger.debug('[LEANTIME_TASKS] Tasks API response status', {
|
||||
status: response.status,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch tasks from Leantime:', {
|
||||
logger.error('[LEANTIME_TASKS] Failed to fetch tasks from Leantime', {
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
statusText: response.statusText,
|
||||
});
|
||||
throw new Error('Failed to fetch tasks from Leantime');
|
||||
}
|
||||
@ -151,33 +171,27 @@ export async function GET(request: NextRequest) {
|
||||
try {
|
||||
data = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse tasks response');
|
||||
logger.error('[LEANTIME_TASKS] Failed to parse tasks response', {
|
||||
error: e instanceof Error ? e.message : String(e),
|
||||
});
|
||||
throw new Error('Invalid response format from Leantime');
|
||||
}
|
||||
|
||||
if (!data.result || !Array.isArray(data.result)) {
|
||||
console.error('Invalid response format from Leantime tasks API');
|
||||
logger.error('[LEANTIME_TASKS] Invalid response format from Leantime tasks API');
|
||||
throw new Error('Invalid response format from Leantime');
|
||||
}
|
||||
|
||||
// Log only the number of tasks and their IDs
|
||||
console.log('Received tasks count:', data.result.length);
|
||||
console.log('Task IDs:', data.result.map((task: any) => task.id));
|
||||
logger.debug('[LEANTIME_TASKS] Received tasks summary', {
|
||||
count: data.result.length,
|
||||
idsSample: data.result.slice(0, 20).map((task: any) => task.id),
|
||||
});
|
||||
|
||||
const tasks = data.result
|
||||
.filter((task: any) => {
|
||||
// Log raw task data for debugging
|
||||
console.log('Raw task data:', {
|
||||
id: task.id,
|
||||
headline: task.headline,
|
||||
status: task.status,
|
||||
type: task.type,
|
||||
dependingTicketId: task.dependingTicketId
|
||||
});
|
||||
|
||||
// Filter out any task (main or subtask) that has status Done (5)
|
||||
if (task.status === 5) {
|
||||
console.log(`Filtering out Done task ${task.id} (type: ${task.type || 'main'}, status: ${task.status})`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -187,7 +201,6 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
// Only show tasks where the user is the editor
|
||||
const isUserEditor = taskEditorId === currentUserId;
|
||||
console.log(`Task ${task.id}: status=${task.status}, type=${task.type || 'main'}, parentId=${task.dependingTicketId || 'none'}, isUserEditor=${isUserEditor}`);
|
||||
return isUserEditor;
|
||||
})
|
||||
.map((task: any) => ({
|
||||
@ -208,14 +221,19 @@ export async function GET(request: NextRequest) {
|
||||
dependingTicketId: task.dependingTicketId || null // Added parent task reference
|
||||
}));
|
||||
|
||||
console.log(`Found ${tasks.length} tasks assigned to user ${userId}`);
|
||||
logger.debug('[LEANTIME_TASKS] Filtered tasks for user', {
|
||||
userId,
|
||||
count: tasks.length,
|
||||
});
|
||||
|
||||
// Cache the results
|
||||
await cacheTasksData(session.user.id, tasks);
|
||||
|
||||
return NextResponse.json(tasks);
|
||||
} catch (error) {
|
||||
console.error('Error in tasks route:', error);
|
||||
logger.error('[LEANTIME_TASKS] Error in tasks route', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to fetch tasks" },
|
||||
{ status: 500 }
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { env } from '@/lib/env';
|
||||
import { getCachedNewsData, cacheNewsData } from '@/lib/redis';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
// Helper function to clean HTML content
|
||||
function cleanHtmlContent(text: string): string {
|
||||
@ -91,21 +92,26 @@ export async function GET(request: Request) {
|
||||
// Also bypass cache if a non-default limit is explicitly requested
|
||||
const bypassCache = forceRefresh || (url.searchParams.has('limit') && limit !== '100');
|
||||
|
||||
console.log(`News API request: limit=${limit}, forceRefresh=${forceRefresh}, bypassCache=${bypassCache}`);
|
||||
logger.debug('[NEWS] Request received', {
|
||||
limit,
|
||||
forceRefresh,
|
||||
bypassCache,
|
||||
});
|
||||
|
||||
// Try to get data from cache if not forcing refresh
|
||||
if (!bypassCache) {
|
||||
const cachedNews = await getCachedNewsData(limit);
|
||||
if (cachedNews) {
|
||||
console.log(`Using cached news data (${cachedNews.length} articles)`);
|
||||
logger.debug('[NEWS] Using cached news data', {
|
||||
limit,
|
||||
count: cachedNews.length,
|
||||
});
|
||||
return NextResponse.json(cachedNews);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Fetching news from FastAPI server with limit=${limit}...`);
|
||||
|
||||
const apiUrl = `${env.NEWS_API_URL}/news?limit=${limit}`;
|
||||
console.log(`Full API URL: ${apiUrl}`);
|
||||
logger.debug('[NEWS] Fetching from backend', { apiUrl });
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
@ -117,10 +123,13 @@ export async function GET(request: Request) {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`News API error: ${response.status} ${response.statusText}`);
|
||||
logger.error('[NEWS] Backend API error', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
});
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType && !contentType.includes('application/json')) {
|
||||
console.error('News API returned non-JSON response');
|
||||
logger.error('[NEWS] Backend returned non-JSON response');
|
||||
return NextResponse.json(
|
||||
{ error: 'News API returned invalid response format', status: response.status },
|
||||
{ status: 502 }
|
||||
@ -135,15 +144,21 @@ export async function GET(request: Request) {
|
||||
let articles;
|
||||
try {
|
||||
articles = await response.json();
|
||||
console.log(`News API returned ${articles.length} articles with limit=${limit}`);
|
||||
console.log(`First article: ${JSON.stringify(articles[0])}`);
|
||||
console.log(`API URL used: ${apiUrl}`);
|
||||
logger.debug('[NEWS] Backend response summary', {
|
||||
limit,
|
||||
count: articles.length,
|
||||
});
|
||||
|
||||
if (articles.length < parseInt(limit) && articles.length > 0) {
|
||||
console.log(`WARNING: API returned fewer articles (${articles.length}) than requested (${limit}). This suggests a backend limit.`);
|
||||
logger.debug('[NEWS] Backend returned fewer articles than requested', {
|
||||
requested: limit,
|
||||
actual: articles.length,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse news API response:', error);
|
||||
logger.error('[NEWS] Failed to parse backend response', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to parse news API response', details: error instanceof Error ? error.message : 'Unknown error' },
|
||||
{ status: 502 }
|
||||
@ -161,14 +176,19 @@ export async function GET(request: Request) {
|
||||
url: article.url
|
||||
}));
|
||||
|
||||
console.log(`Formatted and returning ${formattedNews.length} news articles`);
|
||||
logger.debug('[NEWS] Returning formatted news', {
|
||||
count: formattedNews.length,
|
||||
limit,
|
||||
});
|
||||
|
||||
// Cache the results
|
||||
await cacheNewsData(formattedNews, limit);
|
||||
|
||||
return NextResponse.json(formattedNews);
|
||||
} catch (error) {
|
||||
console.error('News API error:', error);
|
||||
logger.error('[NEWS] Route error', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch news', details: error instanceof Error ? error.message : 'Unknown error' },
|
||||
{ status: 500 }
|
||||
|
||||
@ -2,6 +2,7 @@ import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/app/api/auth/options";
|
||||
import { NextResponse } from "next/server";
|
||||
import { getCachedMessagesData, cacheMessagesData } from "@/lib/redis";
|
||||
import { logger } from "@/lib/logger";
|
||||
|
||||
// Helper function to get user token using admin credentials
|
||||
async function getUserToken(baseUrl: string) {
|
||||
@ -52,18 +53,20 @@ export async function GET(request: Request) {
|
||||
if (!forceRefresh) {
|
||||
const cachedMessages = await getCachedMessagesData(session.user.id);
|
||||
if (cachedMessages) {
|
||||
console.log(`Using cached messages data for user ${session.user.id}`);
|
||||
logger.debug("[ROCKET_CHAT] Using cached messages data", {
|
||||
userId: 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');
|
||||
logger.error('[ROCKET_CHAT] Failed to get Rocket.Chat base URL');
|
||||
return NextResponse.json({ error: 'Server configuration error' }, { status: 500 });
|
||||
}
|
||||
|
||||
console.log('Using Rocket.Chat base URL:', baseUrl);
|
||||
logger.debug('[ROCKET_CHAT] Using Rocket.Chat base URL', { baseUrl });
|
||||
|
||||
// Step 1: Use admin token to authenticate
|
||||
const adminHeaders = {
|
||||
@ -75,7 +78,9 @@ export async function GET(request: Request) {
|
||||
// Step 2: Get the current user's Rocket.Chat ID
|
||||
const username = session.user.email.split('@')[0];
|
||||
if (!username) {
|
||||
console.error('No username found in session email');
|
||||
logger.error('[ROCKET_CHAT] No username found in session email', {
|
||||
email: session.user.email,
|
||||
});
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
@ -86,15 +91,17 @@ export async function GET(request: Request) {
|
||||
});
|
||||
|
||||
if (!usersResponse.ok) {
|
||||
console.error('Failed to get users list:', usersResponse.status);
|
||||
logger.error('[ROCKET_CHAT] Failed to get users list', {
|
||||
status: usersResponse.status,
|
||||
});
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
const usersData = await usersResponse.json();
|
||||
console.log('Users list response:', {
|
||||
logger.debug('[ROCKET_CHAT] Users list summary', {
|
||||
success: usersData.success,
|
||||
count: usersData.count,
|
||||
usersCount: usersData.users?.length
|
||||
usersCount: usersData.users?.length,
|
||||
});
|
||||
|
||||
// Find the current user in the list
|
||||
@ -103,13 +110,16 @@ export async function GET(request: Request) {
|
||||
);
|
||||
|
||||
if (!currentUser) {
|
||||
console.error('User not found in users list');
|
||||
logger.error('[ROCKET_CHAT] User not found in users list', {
|
||||
username,
|
||||
email: session.user.email,
|
||||
});
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
console.log('Found Rocket.Chat user:', {
|
||||
logger.debug('[ROCKET_CHAT] Found user', {
|
||||
username: currentUser.username,
|
||||
id: currentUser._id
|
||||
id: currentUser._id,
|
||||
});
|
||||
|
||||
// Step 3: Create a token for the current user
|
||||
@ -122,9 +132,13 @@ export async function GET(request: Request) {
|
||||
});
|
||||
|
||||
if (!createTokenResponse.ok) {
|
||||
console.error('Failed to create user token:', createTokenResponse.status);
|
||||
logger.error('[ROCKET_CHAT] Failed to create user token', {
|
||||
status: createTokenResponse.status,
|
||||
});
|
||||
const errorText = await createTokenResponse.text();
|
||||
console.error('Create token error details:', errorText);
|
||||
logger.error('[ROCKET_CHAT] Create token error details (truncated)', {
|
||||
bodyPreview: errorText.substring(0, 200),
|
||||
});
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
@ -144,16 +158,20 @@ export async function GET(request: Request) {
|
||||
});
|
||||
|
||||
if (!subscriptionsResponse.ok) {
|
||||
console.error('Failed to get subscriptions:', subscriptionsResponse.status);
|
||||
logger.error('[ROCKET_CHAT] Failed to get subscriptions', {
|
||||
status: subscriptionsResponse.status,
|
||||
});
|
||||
const errorText = await subscriptionsResponse.text();
|
||||
console.error('Subscriptions error details:', errorText);
|
||||
logger.error('[ROCKET_CHAT] Subscriptions error details (truncated)', {
|
||||
bodyPreview: errorText.substring(0, 200),
|
||||
});
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
const subscriptionsData = await subscriptionsResponse.json();
|
||||
|
||||
if (!subscriptionsData.success || !Array.isArray(subscriptionsData.update)) {
|
||||
console.error('Invalid subscriptions response structure');
|
||||
logger.error('[ROCKET_CHAT] Invalid subscriptions response structure');
|
||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||
}
|
||||
|
||||
@ -167,18 +185,10 @@ export async function GET(request: Request) {
|
||||
return ['d', 'c', 'p'].includes(sub.t);
|
||||
});
|
||||
|
||||
console.log('Filtered user subscriptions:', {
|
||||
logger.debug('[ROCKET_CHAT] Filtered user subscriptions', {
|
||||
userId: currentUser._id,
|
||||
username: currentUser.username,
|
||||
totalSubscriptions: userSubscriptions.length,
|
||||
subscriptionDetails: userSubscriptions.map((sub: any) => ({
|
||||
type: sub.t,
|
||||
name: sub.fname || sub.name,
|
||||
rid: sub.rid,
|
||||
alert: sub.alert,
|
||||
unread: sub.unread,
|
||||
userMentions: sub.userMentions
|
||||
}))
|
||||
});
|
||||
|
||||
const messages: any[] = [];
|
||||
@ -217,15 +227,19 @@ export async function GET(request: Request) {
|
||||
);
|
||||
|
||||
if (!messagesResponse.ok) {
|
||||
console.error(`Failed to get messages for room ${subscription.name}:`, messagesResponse.status);
|
||||
logger.error('[ROCKET_CHAT] Failed to get messages for room', {
|
||||
roomName: subscription.name,
|
||||
status: messagesResponse.status,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const messageData = await messagesResponse.json();
|
||||
console.log(`Messages for room ${subscription.fname || subscription.name}:`, {
|
||||
logger.debug('[ROCKET_CHAT] Messages for room', {
|
||||
roomName: subscription.fname || subscription.name,
|
||||
success: messageData.success,
|
||||
count: messageData.count,
|
||||
hasMessages: messageData.messages?.length > 0
|
||||
hasMessages: messageData.messages?.length > 0,
|
||||
});
|
||||
|
||||
if (messageData.success && messageData.messages?.length > 0) {
|
||||
@ -337,7 +351,10 @@ export async function GET(request: Request) {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error fetching messages for room ${subscription.name}:`, error);
|
||||
logger.error('[ROCKET_CHAT] Error fetching messages for room', {
|
||||
roomName: subscription.name,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -358,7 +375,9 @@ export async function GET(request: Request) {
|
||||
|
||||
return NextResponse.json(finalResponse);
|
||||
} catch (error) {
|
||||
console.error('Error fetching messages:', error);
|
||||
logger.error('[ROCKET_CHAT] Error fetching messages', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return NextResponse.json({ error: 'Failed to fetch messages' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,14 +12,7 @@ const clientId = process.env.MICROSOFT_CLIENT_ID;
|
||||
const clientSecret = process.env.MICROSOFT_CLIENT_SECRET;
|
||||
const redirectUri = process.env.MICROSOFT_REDIRECT_URI;
|
||||
|
||||
// Log configuration for debugging
|
||||
console.log('Microsoft OAuth Configuration:', {
|
||||
tenantId,
|
||||
authorizeUrl: MICROSOFT_AUTHORIZE_URL,
|
||||
tokenUrl: MICROSOFT_TOKEN_URL,
|
||||
clientIdFirstChars: clientId ? clientId.substring(0, 5) + '...' : 'undefined',
|
||||
redirectUri
|
||||
});
|
||||
// NOTE: In production we do not log Microsoft OAuth configuration to avoid noise and potential leakage.
|
||||
|
||||
// Required scopes for IMAP and SMTP access
|
||||
const REQUIRED_SCOPES = [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user