alibaba.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { getServerSideConfig } from "@/app/config/server";
  2. import {
  3. Alibaba,
  4. ALIBABA_BASE_URL,
  5. ApiPath,
  6. ModelProvider,
  7. ServiceProvider,
  8. } from "@/app/constant";
  9. import { prettyObject } from "@/app/utils/format";
  10. import { NextRequest, NextResponse } from "next/server";
  11. import { auth } from "@/app/api/auth";
  12. import { isModelAvailableInServer } from "@/app/utils/model";
  13. import type { RequestPayload } from "@/app/client/platforms/openai";
  14. const serverConfig = getServerSideConfig();
  15. export async function handle(
  16. req: NextRequest,
  17. { params }: { params: { path: string[] } },
  18. ) {
  19. console.log("[Alibaba Route] params ", params);
  20. if (req.method === "OPTIONS") {
  21. return NextResponse.json({ body: "OK" }, { status: 200 });
  22. }
  23. const authResult = auth(req, ModelProvider.Qwen);
  24. if (authResult.error) {
  25. return NextResponse.json(authResult, {
  26. status: 401,
  27. });
  28. }
  29. try {
  30. const response = await request(req);
  31. return response;
  32. } catch (e) {
  33. console.error("[Alibaba] ", e);
  34. return NextResponse.json(prettyObject(e));
  35. }
  36. }
  37. async function request(req: NextRequest) {
  38. const controller = new AbortController();
  39. // alibaba use base url or just remove the path
  40. let path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Alibaba, "");
  41. let baseUrl = serverConfig.alibabaUrl || ALIBABA_BASE_URL;
  42. if (!baseUrl.startsWith("http")) {
  43. baseUrl = `https://${baseUrl}`;
  44. }
  45. if (baseUrl.endsWith("/")) {
  46. baseUrl = baseUrl.slice(0, -1);
  47. }
  48. console.log("[Proxy] ", path);
  49. console.log("[Base Url]", baseUrl);
  50. const timeoutId = setTimeout(
  51. () => {
  52. controller.abort();
  53. },
  54. 10 * 60 * 1000,
  55. );
  56. const fetchUrl = `${baseUrl}${path}`;
  57. const fetchOptions: RequestInit = {
  58. headers: {
  59. "Content-Type": "application/json",
  60. Authorization: req.headers.get("Authorization") ?? "",
  61. "X-DashScope-SSE": req.headers.get("X-DashScope-SSE") ?? "disable",
  62. },
  63. method: req.method,
  64. body: req.body,
  65. redirect: "manual",
  66. // @ts-ignore
  67. duplex: "half",
  68. signal: controller.signal,
  69. };
  70. // #1815 try to refuse some request to some models
  71. if (serverConfig.customModels && req.body) {
  72. try {
  73. const clonedBody = await req.text();
  74. fetchOptions.body = clonedBody;
  75. const jsonBody = JSON.parse(clonedBody) as { model?: string };
  76. // not undefined and is false
  77. if (
  78. isModelAvailableInServer(
  79. serverConfig.customModels,
  80. jsonBody?.model as string,
  81. ServiceProvider.Alibaba as string,
  82. )
  83. ) {
  84. return NextResponse.json(
  85. {
  86. error: true,
  87. message: `you are not allowed to use ${jsonBody?.model} model`,
  88. },
  89. {
  90. status: 403,
  91. },
  92. );
  93. }
  94. } catch (e) {
  95. console.error(`[Alibaba] filter`, e);
  96. }
  97. }
  98. try {
  99. const res = await fetch(fetchUrl, fetchOptions);
  100. // to prevent browser prompt for credentials
  101. const newHeaders = new Headers(res.headers);
  102. newHeaders.delete("www-authenticate");
  103. // to disable nginx buffering
  104. newHeaders.set("X-Accel-Buffering", "no");
  105. return new Response(res.body, {
  106. status: res.status,
  107. statusText: res.statusText,
  108. headers: newHeaders,
  109. });
  110. } finally {
  111. clearTimeout(timeoutId);
  112. }
  113. }