NeahStable/app/api/rocket-chat/user-token/route.ts
2026-01-17 13:51:20 +01:00

235 lines
7.7 KiB
TypeScript

import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from "@/app/api/auth/options";
import { logger } from '@/lib/logger';
/**
* Get RocketChat user token for WebSocket connection
* This endpoint returns the user's auth token and userId for real-time connections
*/
export async function GET(request: Request) {
try {
const session = await getServerSession(authOptions);
if (!session?.user?.email) {
return NextResponse.json(
{ error: "Not authenticated" },
{ status: 401 }
);
}
// Extract base URL, removing any paths like /channel, /_oauth/connect, etc.
let baseUrl = process.env.NEXT_PUBLIC_IFRAME_PAROLE_URL;
if (baseUrl) {
// Remove /channel and everything after
baseUrl = baseUrl.split('/channel')[0];
// Remove /_oauth/connect and everything after
baseUrl = baseUrl.split('/_oauth')[0];
// Remove trailing slashes
baseUrl = baseUrl.replace(/\/+$/, '');
}
if (!baseUrl) {
logger.error('[ROCKET_CHAT_USER_TOKEN] Failed to get Rocket.Chat base URL');
return NextResponse.json(
{ error: 'Server configuration error' },
{ status: 500 }
);
}
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
const adminHeaders = {
'X-Auth-Token': process.env.ROCKET_CHAT_TOKEN!,
'X-User-Id': process.env.ROCKET_CHAT_USER_ID!,
'Content-Type': 'application/json'
};
// Get username from email
const username = session.user.email.split('@')[0];
if (!username) {
logger.error('[ROCKET_CHAT_USER_TOKEN] No username found in session email');
return NextResponse.json(
{ error: 'Invalid user' },
{ status: 400 }
);
}
// Get all users to find the current user
let usersResponse;
const usersListUrl = `${baseUrl}/api/v1/users.list`;
try {
logger.debug('[ROCKET_CHAT_USER_TOKEN] Fetching users list', { url: usersListUrl });
usersResponse = await fetch(usersListUrl, {
method: 'GET',
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) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error('[ROCKET_CHAT_USER_TOKEN] Error fetching users list', {
error: errorMessage,
baseUrl,
url: usersListUrl,
});
return NextResponse.json(
{ error: 'Failed to connect to RocketChat' },
{ status: 500 }
);
}
if (!usersResponse.ok) {
const errorText = await usersResponse.text().catch(() => 'Unknown error');
logger.error('[ROCKET_CHAT_USER_TOKEN] Failed to get users list', {
status: usersResponse.status,
statusText: usersResponse.statusText,
errorPreview: errorText.substring(0, 200),
});
return NextResponse.json(
{ error: 'Failed to get user list from RocketChat' },
{ status: 500 }
);
}
const contentType = usersResponse.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
const errorText = await usersResponse.text().catch(() => 'Unknown error');
logger.error('[ROCKET_CHAT_USER_TOKEN] Expected JSON, got HTML', {
contentType,
errorPreview: errorText.substring(0, 200),
});
return NextResponse.json(
{ error: 'RocketChat returned invalid response format' },
{ status: 500 }
);
}
let usersData;
try {
usersData = await usersResponse.json();
} catch (error) {
logger.error('[ROCKET_CHAT_USER_TOKEN] Failed to parse users list JSON', {
error: error instanceof Error ? error.message : String(error),
});
return NextResponse.json(
{ error: 'Failed to parse RocketChat response' },
{ status: 500 }
);
}
const currentUser = usersData.users?.find((u: any) =>
u.username?.toLowerCase() === username.toLowerCase() ||
u.emails?.some((e: any) => e.address?.toLowerCase() === session.user.email?.toLowerCase())
);
if (!currentUser) {
logger.error('[ROCKET_CHAT_USER_TOKEN] User not found in RocketChat', { username });
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
);
}
// Create user token
const secret = process.env.ROCKET_CHAT_CREATE_TOKEN_SECRET;
if (!secret) {
logger.error('[ROCKET_CHAT_USER_TOKEN] ROCKET_CHAT_CREATE_TOKEN_SECRET not configured');
return NextResponse.json(
{ error: 'Server configuration error' },
{ status: 500 }
);
}
let createTokenResponse;
try {
createTokenResponse = await fetch(`${baseUrl}/api/v1/users.createToken`, {
method: 'POST',
headers: adminHeaders,
body: JSON.stringify({
userId: currentUser._id,
secret: secret
})
});
} catch (error) {
logger.error('[ROCKET_CHAT_USER_TOKEN] Error creating token', {
error: error instanceof Error ? error.message : String(error),
});
return NextResponse.json(
{ error: 'Failed to connect to RocketChat' },
{ status: 500 }
);
}
if (!createTokenResponse.ok) {
const errorText = await createTokenResponse.text().catch(() => 'Unknown error');
logger.error('[ROCKET_CHAT_USER_TOKEN] Failed to create user token', {
status: createTokenResponse.status,
statusText: createTokenResponse.statusText,
errorPreview: errorText.substring(0, 200),
});
return NextResponse.json(
{ error: 'Failed to create token' },
{ status: 500 }
);
}
const tokenContentType = createTokenResponse.headers.get('content-type') || '';
if (!tokenContentType.includes('application/json')) {
const errorText = await createTokenResponse.text().catch(() => 'Unknown error');
logger.error('[ROCKET_CHAT_USER_TOKEN] Expected JSON, got HTML', {
contentType: tokenContentType,
errorPreview: errorText.substring(0, 200),
});
return NextResponse.json(
{ error: 'RocketChat returned invalid response format' },
{ status: 500 }
);
}
let tokenData;
try {
tokenData = await createTokenResponse.json();
} catch (error) {
logger.error('[ROCKET_CHAT_USER_TOKEN] Failed to parse token JSON', {
error: error instanceof Error ? error.message : String(error),
});
return NextResponse.json(
{ error: 'Failed to parse RocketChat response' },
{ status: 500 }
);
}
logger.debug('[ROCKET_CHAT_USER_TOKEN] Token created', {
emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
hasAuthToken: !!tokenData.data?.authToken,
tokenLength: tokenData.data?.authToken?.length,
});
return NextResponse.json({
userId: currentUser._id,
authToken: tokenData.data.authToken,
username: currentUser.username,
// Also return the full token data for debugging
tokenData: tokenData.data,
});
} catch (error: any) {
logger.error('[ROCKET_CHAT_USER_TOKEN] Error', {
error: error instanceof Error ? error.message : String(error),
});
return NextResponse.json(
{ error: "Internal server error", message: error.message },
{ status: 500 }
);
}
}