Fondation
This commit is contained in:
parent
672333a6db
commit
866b8fb3dc
@ -105,13 +105,13 @@ async function ensureUserExists(session: any): Promise<void> {
|
|||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
// Authenticate user (declare outside try to access in catch)
|
// Authenticate user (declare outside try to access in catch)
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
if (!session?.user?.id) {
|
if (!session?.user?.id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Unauthorized' },
|
{ error: 'Unauthorized' },
|
||||||
{ status: 401 }
|
{ status: 401 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -265,10 +265,10 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
export async function DELETE(request: Request) {
|
export async function DELETE(request: Request) {
|
||||||
// Authenticate user (declare outside try to access in catch)
|
// Authenticate user (declare outside try to access in catch)
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
if (!session?.user?.id) {
|
if (!session?.user?.id) {
|
||||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
@ -351,13 +351,13 @@ export async function DELETE(request: Request) {
|
|||||||
|
|
||||||
export async function PATCH(request: Request) {
|
export async function PATCH(request: Request) {
|
||||||
// Authenticate user (declare outside try to access in catch)
|
// Authenticate user (declare outside try to access in catch)
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
if (!session?.user?.id) {
|
if (!session?.user?.id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Unauthorized' },
|
{ error: 'Unauthorized' },
|
||||||
{ status: 401 }
|
{ status: 401 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|||||||
@ -24,13 +24,13 @@ const emailListCache: Record<string, EmailCacheEntry> = {};
|
|||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
// Authenticate user (declare outside try to access in catch)
|
// Authenticate user (declare outside try to access in catch)
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
if (!session || !session.user?.id) {
|
if (!session || !session.user?.id) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Not authenticated" },
|
{ error: "Not authenticated" },
|
||||||
{ status: 401 }
|
{ status: 401 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|||||||
@ -79,7 +79,12 @@ export async function GET(request: Request) {
|
|||||||
return NextResponse.json({ error: 'Server configuration error' }, { status: 500 });
|
return NextResponse.json({ error: 'Server configuration error' }, { status: 500 });
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('[ROCKET_CHAT] Using Rocket.Chat base URL');
|
logger.debug('[ROCKET_CHAT] Using Rocket.Chat base URL', {
|
||||||
|
baseUrl,
|
||||||
|
hasToken: !!process.env.ROCKET_CHAT_TOKEN,
|
||||||
|
hasUserId: !!process.env.ROCKET_CHAT_USER_ID,
|
||||||
|
hasSecret: !!process.env.ROCKET_CHAT_CREATE_TOKEN_SECRET,
|
||||||
|
});
|
||||||
|
|
||||||
// Step 1: Use admin token to authenticate
|
// Step 1: Use admin token to authenticate
|
||||||
const adminHeaders = {
|
const adminHeaders = {
|
||||||
@ -100,15 +105,37 @@ export async function GET(request: Request) {
|
|||||||
// Get all users to find the current user
|
// Get all users to find the current user
|
||||||
let usersData;
|
let usersData;
|
||||||
try {
|
try {
|
||||||
usersData = await fetchJsonWithTimeout(`${baseUrl}/api/v1/users.list`, {
|
const usersListUrl = `${baseUrl}/api/v1/users.list`;
|
||||||
|
logger.debug('[ROCKET_CHAT] Fetching users list', { url: usersListUrl });
|
||||||
|
|
||||||
|
usersData = await fetchJsonWithTimeout(usersListUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
timeout: 10000, // 10 seconds
|
timeout: 10000, // 10 seconds
|
||||||
headers: adminHeaders
|
headers: adminHeaders
|
||||||
});
|
});
|
||||||
} catch (error) {
|
|
||||||
logger.error('[ROCKET_CHAT] Error fetching users list', {
|
logger.debug('[ROCKET_CHAT] Successfully fetched users list', {
|
||||||
error: error instanceof Error ? error.message : String(error),
|
success: usersData.success,
|
||||||
|
count: usersData.count,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
logger.error('[ROCKET_CHAT] Error fetching users list', {
|
||||||
|
error: errorMessage,
|
||||||
|
baseUrl,
|
||||||
|
url: `${baseUrl}/api/v1/users.list`,
|
||||||
|
hasToken: !!process.env.ROCKET_CHAT_TOKEN,
|
||||||
|
hasUserId: !!process.env.ROCKET_CHAT_USER_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If it's an HTML response error, log more details
|
||||||
|
if (errorMessage.includes('text/html')) {
|
||||||
|
logger.error('[ROCKET_CHAT] RocketChat returned HTML instead of JSON - possible authentication or URL issue', {
|
||||||
|
baseUrl,
|
||||||
|
checkUrl: `${baseUrl}/api/v1/users.list`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Return empty messages instead of failing completely
|
// Return empty messages instead of failing completely
|
||||||
return NextResponse.json({ messages: [] }, { status: 200 });
|
return NextResponse.json({ messages: [] }, { status: 200 });
|
||||||
}
|
}
|
||||||
@ -412,10 +439,28 @@ export async function GET(request: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(finalResponse);
|
return NextResponse.json(finalResponse);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
logger.error('[ROCKET_CHAT] Error fetching messages', {
|
logger.error('[ROCKET_CHAT] Error fetching messages', {
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: errorMessage,
|
||||||
|
baseUrl: process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL?.split('/channel')[0],
|
||||||
|
hasToken: !!process.env.ROCKET_CHAT_TOKEN,
|
||||||
|
hasUserId: !!process.env.ROCKET_CHAT_USER_ID,
|
||||||
|
hasSecret: !!process.env.ROCKET_CHAT_CREATE_TOKEN_SECRET,
|
||||||
});
|
});
|
||||||
return NextResponse.json({ error: 'Failed to fetch messages' }, { status: 500 });
|
|
||||||
|
// If it's an HTML response error, provide more helpful message
|
||||||
|
if (errorMessage.includes('text/html') || errorMessage.includes('HTML')) {
|
||||||
|
logger.error('[ROCKET_CHAT] RocketChat is returning HTML - possible causes:', {
|
||||||
|
cause1: 'Invalid or expired ROCKET_CHAT_TOKEN',
|
||||||
|
cause2: 'Invalid or expired ROCKET_CHAT_USER_ID',
|
||||||
|
cause3: 'Incorrect baseUrl (check NEXT_PUBLIC_IFRAME_PAROLE_URL)',
|
||||||
|
cause4: 'RocketChat server is down or unreachable',
|
||||||
|
cause5: 'RocketChat API endpoint changed or requires different authentication',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return empty messages instead of error to prevent widget from breaking
|
||||||
|
return NextResponse.json({ messages: [], total: 0, hasMore: false, totalUnreadCount: 0 }, { status: 200 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,13 @@ export async function GET(request: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug('[ROCKET_CHAT_USER_TOKEN] Using Rocket.Chat base URL', {
|
||||||
|
baseUrl,
|
||||||
|
hasToken: !!process.env.ROCKET_CHAT_TOKEN,
|
||||||
|
hasUserId: !!process.env.ROCKET_CHAT_USER_ID,
|
||||||
|
hasSecret: !!process.env.ROCKET_CHAT_CREATE_TOKEN_SECRET,
|
||||||
|
});
|
||||||
|
|
||||||
// Use admin token to authenticate
|
// Use admin token to authenticate
|
||||||
const adminHeaders = {
|
const adminHeaders = {
|
||||||
'X-Auth-Token': process.env.ROCKET_CHAT_TOKEN!,
|
'X-Auth-Token': process.env.ROCKET_CHAT_TOKEN!,
|
||||||
@ -46,14 +53,26 @@ export async function GET(request: Request) {
|
|||||||
|
|
||||||
// Get all users to find the current user
|
// Get all users to find the current user
|
||||||
let usersResponse;
|
let usersResponse;
|
||||||
|
const usersListUrl = `${baseUrl}/api/v1/users.list`;
|
||||||
try {
|
try {
|
||||||
usersResponse = await fetch(`${baseUrl}/api/v1/users.list`, {
|
logger.debug('[ROCKET_CHAT_USER_TOKEN] Fetching users list', { url: usersListUrl });
|
||||||
|
|
||||||
|
usersResponse = await fetch(usersListUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: adminHeaders
|
headers: adminHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.debug('[ROCKET_CHAT_USER_TOKEN] Users list response', {
|
||||||
|
status: usersResponse.status,
|
||||||
|
statusText: usersResponse.statusText,
|
||||||
|
contentType: usersResponse.headers.get('content-type'),
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
logger.error('[ROCKET_CHAT_USER_TOKEN] Error fetching users list', {
|
logger.error('[ROCKET_CHAT_USER_TOKEN] Error fetching users list', {
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: errorMessage,
|
||||||
|
baseUrl,
|
||||||
|
url: usersListUrl,
|
||||||
});
|
});
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Failed to connect to RocketChat' },
|
{ error: 'Failed to connect to RocketChat' },
|
||||||
@ -125,13 +144,13 @@ export async function GET(request: Request) {
|
|||||||
let createTokenResponse;
|
let createTokenResponse;
|
||||||
try {
|
try {
|
||||||
createTokenResponse = await fetch(`${baseUrl}/api/v1/users.createToken`, {
|
createTokenResponse = await fetch(`${baseUrl}/api/v1/users.createToken`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: adminHeaders,
|
headers: adminHeaders,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
userId: currentUser._id,
|
userId: currentUser._id,
|
||||||
secret: secret
|
secret: secret
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('[ROCKET_CHAT_USER_TOKEN] Error creating token', {
|
logger.error('[ROCKET_CHAT_USER_TOKEN] Error creating token', {
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
|||||||
@ -221,7 +221,7 @@ export default function MissionTabDetailPage() {
|
|||||||
<div className="w-24 h-24 rounded-md overflow-hidden flex-shrink-0">
|
<div className="w-24 h-24 rounded-md overflow-hidden flex-shrink-0">
|
||||||
{mission.logoUrl ? (
|
{mission.logoUrl ? (
|
||||||
<Image
|
<Image
|
||||||
src={mission.logoUrl}
|
src={mission.logoUrl}
|
||||||
alt={mission.name}
|
alt={mission.name}
|
||||||
width={96}
|
width={96}
|
||||||
height={96}
|
height={96}
|
||||||
|
|||||||
@ -237,7 +237,7 @@ export default function MissionsPage() {
|
|||||||
<div className="w-48 h-48 relative">
|
<div className="w-48 h-48 relative">
|
||||||
{mission.logoUrl ? (
|
{mission.logoUrl ? (
|
||||||
<Image
|
<Image
|
||||||
src={mission.logoUrl}
|
src={mission.logoUrl}
|
||||||
alt={mission.name}
|
alt={mission.name}
|
||||||
width={192}
|
width={192}
|
||||||
height={192}
|
height={192}
|
||||||
|
|||||||
60
app/page.tsx
60
app/page.tsx
@ -52,11 +52,11 @@ function EmailSkeleton() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ParoleSkeleton() {
|
function ParoleSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="col-span-6 animate-pulse">
|
<div className="col-span-6 animate-pulse">
|
||||||
<div className="h-96 bg-gray-200 rounded-lg"></div>
|
<div className="h-96 bg-gray-200 rounded-lg"></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
@ -70,47 +70,47 @@ export default async function Home() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HomeLogoutCheck />
|
<HomeLogoutCheck />
|
||||||
<main className="h-screen overflow-auto">
|
<main className="h-screen overflow-auto">
|
||||||
<div className="container mx-auto p-4 mt-12">
|
<div className="container mx-auto p-4 mt-12">
|
||||||
{/* First row */}
|
{/* First row */}
|
||||||
<div className="grid grid-cols-12 gap-4 mb-4">
|
<div className="grid grid-cols-12 gap-4 mb-4">
|
||||||
<Suspense fallback={<QuoteCardSkeleton />}>
|
<Suspense fallback={<QuoteCardSkeleton />}>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<QuoteCard />
|
<QuoteCard />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Suspense fallback={<CalendarSkeleton />}>
|
<Suspense fallback={<CalendarSkeleton />}>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Calendar />
|
<Calendar />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Suspense fallback={<NewsSkeleton />}>
|
<Suspense fallback={<NewsSkeleton />}>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<News />
|
<News />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Suspense fallback={<DutiesSkeleton />}>
|
<Suspense fallback={<DutiesSkeleton />}>
|
||||||
<div className="col-span-3">
|
<div className="col-span-3">
|
||||||
<Duties />
|
<Duties />
|
||||||
</div>
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
|
</Suspense>
|
||||||
{/* Second row */}
|
</div>
|
||||||
<div className="grid grid-cols-12 gap-4">
|
|
||||||
|
{/* Second row */}
|
||||||
|
<div className="grid grid-cols-12 gap-4">
|
||||||
<Suspense fallback={<EmailSkeleton />}>
|
<Suspense fallback={<EmailSkeleton />}>
|
||||||
<div className="col-span-6">
|
<div className="col-span-6">
|
||||||
<Email />
|
<Email />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Suspense fallback={<ParoleSkeleton />}>
|
<Suspense fallback={<ParoleSkeleton />}>
|
||||||
<div className="col-span-6">
|
<div className="col-span-6">
|
||||||
<Parole />
|
<Parole />
|
||||||
</div>
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export function CalendarWidget() {
|
|||||||
const fetchUpcomingEvents = async () => {
|
const fetchUpcomingEvents = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
// Récupérer d'abord les calendriers de l'utilisateur
|
// Récupérer d'abord les calendriers de l'utilisateur
|
||||||
const calendarsRes = await fetch("/api/calendars");
|
const calendarsRes = await fetch("/api/calendars");
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export function CalendarWidget() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const calendars = await calendarsRes.json();
|
const calendars = await calendarsRes.json();
|
||||||
|
|
||||||
if (calendars.length === 0) {
|
if (calendars.length === 0) {
|
||||||
setEvents([]);
|
setEvents([]);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -55,7 +55,7 @@ export function CalendarWidget() {
|
|||||||
// Date actuelle et date dans 7 jours
|
// Date actuelle et date dans 7 jours
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const nextWeek = addDays(now, 7);
|
const nextWeek = addDays(now, 7);
|
||||||
|
|
||||||
// Helper function to get display name for calendar
|
// Helper function to get display name for calendar
|
||||||
const getCalendarDisplayName = (calendar: any) => {
|
const getCalendarDisplayName = (calendar: any) => {
|
||||||
// If calendar is synced to an external account, use the account name
|
// If calendar is synced to an external account, use the account name
|
||||||
@ -88,7 +88,7 @@ export function CalendarWidget() {
|
|||||||
return events.map((event: any) => ({
|
return events.map((event: any) => ({
|
||||||
...event,
|
...event,
|
||||||
calendarName: getCalendarDisplayName(calendar),
|
calendarName: getCalendarDisplayName(calendar),
|
||||||
calendarColor: calendar.color,
|
calendarColor: calendar.color,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,10 +105,10 @@ export function CalendarWidget() {
|
|||||||
|
|
||||||
// Limiter à 5 événements pour l'affichage
|
// Limiter à 5 événements pour l'affichage
|
||||||
const upcomingEvents = sortedEvents.slice(0, 5);
|
const upcomingEvents = sortedEvents.slice(0, 5);
|
||||||
|
|
||||||
// Calculate current event count (all events, not just displayed)
|
// Calculate current event count (all events, not just displayed)
|
||||||
const currentEventCount = sortedEvents.length;
|
const currentEventCount = sortedEvents.length;
|
||||||
|
|
||||||
// Prepare notification items for all upcoming events
|
// Prepare notification items for all upcoming events
|
||||||
const notificationItems = sortedEvents.map(event => ({
|
const notificationItems = sortedEvents.map(event => ({
|
||||||
id: event.id,
|
id: event.id,
|
||||||
|
|||||||
@ -70,8 +70,8 @@ export function Parole() {
|
|||||||
// Check if response is JSON before trying to parse
|
// Check if response is JSON before trying to parse
|
||||||
const contentType = response.headers.get('content-type') || '';
|
const contentType = response.headers.get('content-type') || '';
|
||||||
if (contentType.includes('application/json')) {
|
if (contentType.includes('application/json')) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
throw new Error(errorData.error || 'Failed to fetch messages');
|
throw new Error(errorData.error || 'Failed to fetch messages');
|
||||||
} else {
|
} else {
|
||||||
// Response is HTML (probably an error page)
|
// Response is HTML (probably an error page)
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
|
|||||||
@ -78,8 +78,18 @@ export async function fetchJsonWithTimeout<T = any>(
|
|||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const response = await fetchWithTimeout(url, options);
|
const response = await fetchWithTimeout(url, options);
|
||||||
|
|
||||||
|
// Clone response early so we can read it multiple times if needed
|
||||||
|
const clonedResponse = response.clone();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text().catch(() => 'Unknown error');
|
const errorText = await clonedResponse.text().catch(() => 'Unknown error');
|
||||||
|
const contentType = response.headers.get('content-type') || '';
|
||||||
|
|
||||||
|
// If it's HTML, provide more context
|
||||||
|
if (contentType.includes('text/html') || errorText.trim().startsWith('<!DOCTYPE') || errorText.trim().startsWith('<html')) {
|
||||||
|
throw new Error(`HTTP ${response.status} ${response.statusText}: Server returned HTML page instead of JSON. This usually means authentication failed or URL is incorrect. Preview: ${errorText.substring(0, 300)}`);
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`HTTP ${response.status} ${response.statusText}: ${errorText.substring(0, 200)}`
|
`HTTP ${response.status} ${response.statusText}: ${errorText.substring(0, 200)}`
|
||||||
);
|
);
|
||||||
@ -89,26 +99,23 @@ export async function fetchJsonWithTimeout<T = any>(
|
|||||||
|
|
||||||
// Check if response is JSON
|
// Check if response is JSON
|
||||||
if (!contentType.includes('application/json')) {
|
if (!contentType.includes('application/json')) {
|
||||||
// Try to read the response text to see what we got
|
// Read the response text to see what we got
|
||||||
const responseText = await response.text().catch(() => 'Unable to read response');
|
const responseText = await clonedResponse.text().catch(() => 'Unable to read response');
|
||||||
const preview = responseText.substring(0, 200);
|
const preview = responseText.substring(0, 500);
|
||||||
|
|
||||||
// If it looks like HTML, provide a more helpful error
|
// If it looks like HTML, provide a more helpful error
|
||||||
if (responseText.trim().startsWith('<!DOCTYPE') || responseText.trim().startsWith('<html')) {
|
if (responseText.trim().startsWith('<!DOCTYPE') || responseText.trim().startsWith('<html')) {
|
||||||
throw new Error(`Expected JSON response, got text/html. Server may be returning an error page. Preview: ${preview}`);
|
throw new Error(`Expected JSON response, got text/html. Server may be returning an error page or login page. URL: ${url}. Preview: ${preview}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Expected JSON response, got ${contentType || 'unknown'}. Preview: ${preview}`);
|
throw new Error(`Expected JSON response, got ${contentType || 'unknown'}. URL: ${url}. Preview: ${preview}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone the response before reading it, in case we need to read it again
|
|
||||||
const clonedResponse = response.clone();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (jsonError) {
|
} catch (jsonError) {
|
||||||
// If JSON parsing fails, try to read the text to see what we got
|
// If JSON parsing fails, try to read the text to see what we got
|
||||||
const responseText = await clonedResponse.text().catch(() => 'Unable to read response');
|
const responseText = await clonedResponse.text().catch(() => 'Unable to read response');
|
||||||
throw new Error(`Failed to parse JSON response. Content: ${responseText.substring(0, 200)}`);
|
throw new Error(`Failed to parse JSON response from ${url}. Content: ${responseText.substring(0, 500)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user