route.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { NextRequest } from 'next/server';
  2. import { createLogger } from '@/lib/logger';
  3. import { apiError, apiSuccess } from '@/lib/server/api-response';
  4. import { resolvePDFApiKey, resolvePDFBaseUrl } from '@/lib/server/provider-config';
  5. import { validateUrlForSSRF } from '@/lib/server/ssrf-guard';
  6. const log = createLogger('Verify PDF Provider');
  7. export async function POST(req: NextRequest) {
  8. let providerId: string | undefined;
  9. try {
  10. const body = await req.json();
  11. providerId = body.providerId;
  12. const { apiKey, baseUrl } = body;
  13. if (!providerId) {
  14. return apiError('MISSING_REQUIRED_FIELD', 400, 'Provider ID is required');
  15. }
  16. const clientBaseUrl = (baseUrl as string | undefined) || undefined;
  17. if (clientBaseUrl && process.env.NODE_ENV === 'production') {
  18. const ssrfError = await validateUrlForSSRF(clientBaseUrl);
  19. if (ssrfError) {
  20. return apiError('INVALID_URL', 403, ssrfError);
  21. }
  22. }
  23. const resolvedBaseUrl = clientBaseUrl ? clientBaseUrl : resolvePDFBaseUrl(providerId, baseUrl);
  24. if (!resolvedBaseUrl) {
  25. return apiError('MISSING_REQUIRED_FIELD', 400, 'Base URL is required');
  26. }
  27. const resolvedApiKey = clientBaseUrl
  28. ? (apiKey as string | undefined) || ''
  29. : resolvePDFApiKey(providerId, apiKey);
  30. const headers: Record<string, string> = {};
  31. if (resolvedApiKey) {
  32. headers['Authorization'] = `Bearer ${resolvedApiKey}`;
  33. }
  34. const response = await fetch(resolvedBaseUrl, {
  35. headers,
  36. signal: AbortSignal.timeout(10000),
  37. redirect: 'manual',
  38. });
  39. if (response.status >= 300 && response.status < 400) {
  40. return apiError('REDIRECT_NOT_ALLOWED', 403, 'Redirects are not allowed');
  41. }
  42. // MinerU's FastAPI root returns 404 (no root route), but the server is reachable.
  43. // Any HTTP response (including 404) means the server is up.
  44. return apiSuccess({
  45. message: 'Connection successful',
  46. status: response.status,
  47. });
  48. } catch (error) {
  49. log.error(`PDF provider verification failed [provider=${providerId ?? 'unknown'}]:`, error);
  50. let errorMessage = 'Connection failed';
  51. if (error instanceof Error) {
  52. if (error.message.includes('ECONNREFUSED')) {
  53. errorMessage = 'Cannot connect to server, please check the Base URL';
  54. } else if (error.message.includes('ENOTFOUND')) {
  55. errorMessage = 'Server not found, please check the Base URL';
  56. } else if (error.message.includes('timeout') || error.name === 'TimeoutError') {
  57. errorMessage = 'Connection timed out';
  58. } else {
  59. errorMessage = error.message;
  60. }
  61. }
  62. return apiError('INTERNAL_ERROR', 500, errorMessage);
  63. }
  64. }