Browse Source

网关通过接口获取

feature_apikey
TianYun 2 months ago
parent
commit
557dce6fbe
  1. 0
      AGENTS.md
  2. 12
      src/app/api/_auth.ts
  3. 12
      src/app/api/_gatewayApiKey.ts
  4. 6
      src/app/api/chat/__tests__/route.test.ts
  5. 4
      src/app/api/chat/route.ts
  6. 20
      src/app/api/generate/__tests__/route.test.ts
  7. 4
      src/app/api/generate/route.ts
  8. 7
      src/app/api/llm/__tests__/route.test.ts
  9. 4
      src/app/api/llm/route.ts
  10. 14
      src/app/api/models/__tests__/route.test.ts
  11. 4
      src/app/api/models/route.ts
  12. 4
      src/app/api/quickstart/propose/route.ts
  13. 4
      src/app/api/quickstart/route.ts
  14. 16
      src/lib/quickstart/__tests__/modelClient.test.ts
  15. 55
      src/store/__tests__/userStore.test.ts
  16. 83
      src/store/userStore.ts
  17. 4
      src/store/utils/buildApiHeaders.ts
  18. 13
      src/types/user.ts
  19. 7
      src/utils/__tests__/authFetch.test.ts
  20. 16
      src/utils/authFetch.ts
  21. 2
      src/utils/gatewayHeaders.ts

0
AGENTS.md

12
src/app/api/_auth.ts

@ -1,4 +1,8 @@
import type { NextRequest } from "next/server";
import {
LEGACY_NEWAPIWG_API_KEY_HEADER,
NEWAPIWG_API_KEY_HEADER,
} from "@/utils/gatewayHeaders";
import { ApiError } from "./_errors";
export interface LoginContext {
@ -21,6 +25,14 @@ export function getRequestToken(request: Request | NextRequest): string | null {
return bearerToken?.trim() || null;
}
export function getRequestGatewayApiKey(request: Request | NextRequest): string | null {
return (
request.headers.get(NEWAPIWG_API_KEY_HEADER)?.trim() ||
request.headers.get(LEGACY_NEWAPIWG_API_KEY_HEADER)?.trim() ||
null
);
}
export function requireLogin(request: Request | NextRequest): LoginContext {
const token = getRequestToken(request);
if (!token) {

12
src/app/api/_gatewayApiKey.ts

@ -1,6 +1,6 @@
import type { NextRequest } from "next/server";
import { createHash } from "crypto";
import { getRequestToken } from "./_auth";
import { getRequestGatewayApiKey, getRequestToken } from "./_auth";
import { ApiError } from "./_errors";
const UPSTREAM_USER_APIKEY_PATH = "/api_client/gateway/apikey/list";
@ -102,3 +102,13 @@ export async function fetchUserGatewayApiKey(
return item.apikey;
}
export async function resolveUserGatewayApiKey(
request: Request | NextRequest
): Promise<string> {
const apiKey = getRequestGatewayApiKey(request);
if (!apiKey) {
throw ApiError.badRequest("Current user gateway API key is required.");
}
return apiKey;
}

6
src/app/api/chat/__tests__/route.test.ts

@ -62,6 +62,7 @@ describe("/api/chat route", () => {
const request = new Request("http://localhost/api/chat", {
method: "POST",
headers: {
Authorization: "Bearer login-token",
"X-NewApiWG-Key": "browser-newapi-key",
"X-NewApiWG-Base-URL": "https://llmapitest.popi.art/v1",
},
@ -79,6 +80,11 @@ describe("/api/chat route", () => {
name: "newapiwg",
apiKey: "browser-newapi-key",
baseURL: "https://llmapitest.popi.art/v1",
headers: {
"X-User-Authorization": "Bearer login-token",
"X-User-Token": "login-token",
token: "login-token",
},
});
expect(mockNewApiWGProvider).toHaveBeenCalledWith("doubao-seed-2-0-lite-260428");
expect(mockStreamText).toHaveBeenCalledWith(

4
src/app/api/chat/route.ts

@ -9,7 +9,7 @@ import { normalizeNewApiWGBaseUrl } from '@/app/api/generate/providers/newapiwg'
import { DEFAULT_NEWAPIWG_LLM_MODEL_ID } from '@/lib/llmModels';
import { buildUserForwardHeaders, requireLogin } from '@/app/api/_auth';
import { ApiError, apiErrorResponse } from '@/app/api/_errors';
import { fetchUserGatewayApiKey } from '@/app/api/_gatewayApiKey';
import { resolveUserGatewayApiKey } from '@/app/api/_gatewayApiKey';
export const maxDuration = 60; // 1 minute timeout
@ -22,7 +22,7 @@ export async function POST(request: Request) {
selectedNodeIds?: string[];
};
const apiKey = await fetchUserGatewayApiKey(request);
const apiKey = await resolveUserGatewayApiKey(request);
const baseUrl = normalizeNewApiWGBaseUrl(
request.headers.get('X-NewApiWG-Base-URL') || process.env.NEWAPIWG_BASE_URL
);

20
src/app/api/generate/__tests__/route.test.ts

@ -53,7 +53,10 @@ function createMockPostRequest(
): NextRequest {
return {
json: vi.fn().mockResolvedValue(body),
headers: new Headers(headers),
headers: new Headers({
Authorization: "Bearer login-token",
...(headers || {}),
}),
} as unknown as NextRequest;
}
@ -121,8 +124,7 @@ describe("/api/generate route", () => {
});
});
it("should read NewApiWG key and base URL from headers or env", () => {
process.env.NEWAPIWG_API_KEY = "env-newapiwg-key";
it("should read NewApiWG base URL from headers or env", () => {
process.env.NEWAPIWG_BASE_URL = "https://env.example/v1";
const request = createMockPostRequest(
@ -131,7 +133,7 @@ describe("/api/generate route", () => {
);
expect(getConnectionParams(request, "newapiwg")).toEqual({
apiKey: "env-newapiwg-key",
apiKey: undefined,
baseUrl: "https://header.example/v1",
});
});
@ -1057,7 +1059,10 @@ describe("/api/generate route", () => {
size: "1024x1024",
},
},
{ "X-NewApiWG-Key": "test-newapiwg-key" }
{
Authorization: "Bearer login-token",
"X-NewApiWG-Key": "test-newapiwg-key",
}
);
const response = await POST(request);
@ -1108,7 +1113,10 @@ describe("/api/generate route", () => {
capabilities: ["text-to-image", "image-to-image"],
},
},
{ "X-NewApiWG-Key": "test-newapiwg-key" }
{
Authorization: "Bearer login-token",
"X-NewApiWG-Key": "test-newapiwg-key",
}
);
const response = await POST(request);

4
src/app/api/generate/route.ts

@ -21,7 +21,7 @@ import { generateWithWaveSpeed } from "./providers/wavespeed";
import { generateWithNewApiWG } from "./providers/newapiwg";
import { requireConfiguredValue, requireLogin } from "../_auth";
import { ApiError, apiErrorResponse } from "../_errors";
import { fetchUserGatewayApiKey } from "../_gatewayApiKey";
import { resolveUserGatewayApiKey } from "../_gatewayApiKey";
// Re-export for backward compatibility (test file imports from route)
export const clearFalInputMappingCache = _clearFalInputMappingCache;
@ -218,7 +218,7 @@ export async function POST(request: NextRequest) {
const selected = requireSelectedModel(selectedModel, "NewApiWG");
const { baseUrl: newApiWGBaseUrl } = getConnectionParams(request, "newapiwg");
const apiKey = await fetchUserGatewayApiKey(request);
const apiKey = await resolveUserGatewayApiKey(request);
const genInput: GenerationInput = {
model: {

7
src/app/api/llm/__tests__/route.test.ts

@ -60,7 +60,10 @@ function createMockPostRequest(
): NextRequest {
return {
json: vi.fn().mockResolvedValue(body),
headers: new Headers(headers),
headers: new Headers({
Authorization: "Bearer login-token",
...(headers || {}),
}),
} as unknown as NextRequest;
}
@ -336,6 +339,7 @@ describe("/api/llm route", () => {
maxTokens: 1024,
},
{
Authorization: "Bearer login-token",
"X-NewApiWG-Key": "test-newapiwg-key",
"X-NewApiWG-Base-URL": "https://newapi.example/v1",
}
@ -376,6 +380,7 @@ describe("/api/llm route", () => {
model: "Doubao-Seed-2.0-lite",
},
{
Authorization: "Bearer login-token",
"X-NewApiWG-Key": "test-newapiwg-key",
"X-NewApiWG-Base-URL": "https://newapi.example/v1",
}

4
src/app/api/llm/route.ts

@ -5,7 +5,7 @@ import { logger } from "@/utils/logger";
import { normalizeNewApiWGBaseUrl } from "@/app/api/generate/providers/newapiwg";
import { buildGatewayHeaders, requireLogin } from "@/app/api/_auth";
import { ApiError, apiErrorResponse } from "@/app/api/_errors";
import { fetchUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
import { resolveUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
export const maxDuration = 60; // 1 minute timeout
@ -436,7 +436,7 @@ export async function POST(request: NextRequest) {
let text: string;
if (provider === "newapiwg") {
const newApiWGKey = await fetchUserGatewayApiKey(request);
const newApiWGKey = await resolveUserGatewayApiKey(request);
text = await generateWithNewApiWG(prompt, model, temperature, maxTokens, images, videos, requestId, newApiWGKey, newApiWGBaseUrl, login.token);
} else if (provider === "google") {
text = await generateWithGoogle(prompt, model, temperature, maxTokens, images, videos, requestId, geminiApiKey);

14
src/app/api/models/__tests__/route.test.ts

@ -37,7 +37,10 @@ function createMockGetRequest(
return {
nextUrl: url,
headers: new Headers(headers),
headers: new Headers({
Authorization: "Bearer login-token",
...(headers || {}),
}),
} as unknown as NextRequest;
}
@ -134,7 +137,7 @@ describe("/api/models route", () => {
}),
});
const request = createMockGetRequest();
const request = createMockGetRequest({}, { "X-NewApiWG-Key": "test-newapiwg-key" });
const response = await GET(request);
const data = await response.json();
@ -153,7 +156,10 @@ describe("/api/models route", () => {
process.env.NEXT_PUBLIC_PROVIDER_MODE = "popi";
process.env.NEWAPIWG_API_KEY = "test-newapiwg-key";
const request = createMockGetRequest({ provider: "replicate" });
const request = createMockGetRequest(
{ provider: "replicate" },
{ "X-NewApiWG-Key": "test-newapiwg-key" }
);
const response = await GET(request);
const data = await response.json();
@ -174,7 +180,7 @@ describe("/api/models route", () => {
expect(response.status).toBe(400);
expect(data.success).toBe(false);
expect(data.error).toContain("Popi Models API key required");
expect(data.error).toContain("Current user gateway API key is required");
expect(mockFetch).not.toHaveBeenCalled();
});
});

4
src/app/api/models/route.ts

@ -46,7 +46,7 @@ import {
} from "@/lib/providerMode";
import { requireLogin } from "@/app/api/_auth";
import { apiErrorResponse } from "@/app/api/_errors";
import { fetchUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
import { resolveUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
// API base URLs
const REPLICATE_API_BASE = "https://api.replicate.com/v1";
@ -1122,7 +1122,7 @@ export async function GET(
if (needsNewApiWGKey) {
try {
newApiWGKey = await fetchUserGatewayApiKey(request);
newApiWGKey = await resolveUserGatewayApiKey(request);
} catch (error) {
if (popiProviderMode || providerFilter === "newapiwg") {
return apiErrorResponse(error) as unknown as NextResponse<ModelsResponse>;

4
src/app/api/quickstart/propose/route.ts

@ -5,7 +5,7 @@ import {
getQuickstartMissingConfigError,
resolveQuickstartModelConfig,
} from "@/lib/quickstart/modelClient";
import { fetchUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
import { resolveUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
import { ApiError } from "@/app/api/_errors";
import { parseJSONFromResponse } from "@/lib/quickstart/validation";
import type { WorkflowProposal, WorkflowComplexity, NodeType } from "@/types";
@ -196,7 +196,7 @@ export async function POST(request: NextRequest) {
);
}
const newApiWGKey = await fetchUserGatewayApiKey(request);
const newApiWGKey = await resolveUserGatewayApiKey(request);
const modelConfig = resolveQuickstartModelConfig(request.headers, {
newApiWGApiKey: newApiWGKey,
});

4
src/app/api/quickstart/route.ts

@ -7,7 +7,7 @@ import {
getQuickstartMissingConfigError,
resolveQuickstartModelConfig,
} from "@/lib/quickstart/modelClient";
import { fetchUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
import { resolveUserGatewayApiKey } from "@/app/api/_gatewayApiKey";
import { ApiError } from "@/app/api/_errors";
import {
validateWorkflowJSON,
@ -131,7 +131,7 @@ export async function POST(request: NextRequest) {
);
}
const newApiWGKey = await fetchUserGatewayApiKey(request);
const newApiWGKey = await resolveUserGatewayApiKey(request);
const modelConfig = resolveQuickstartModelConfig(request.headers, {
newApiWGApiKey: newApiWGKey,
});

16
src/lib/quickstart/__tests__/modelClient.test.ts

@ -9,10 +9,12 @@ describe("quickstart model client", () => {
it("prefers NewApiWG request headers over Gemini env config", () => {
const config = resolveQuickstartModelConfig(
new Headers({
"X-NewApiWG-Key": "sk-user",
"X-NewApiWG-Base-URL": "https://gateway.example/v1/",
}),
{ GEMINI_API_KEY: "gemini-env" }
{
env: { GEMINI_API_KEY: "gemini-env" },
newApiWGApiKey: "sk-user",
}
);
expect(config).toEqual({
@ -23,16 +25,18 @@ describe("quickstart model client", () => {
});
});
it("prefers NewApiWG env config over Gemini env config", () => {
it("uses NewApiWG base URL from env when a user gateway key is available", () => {
const config = resolveQuickstartModelConfig(new Headers(), {
NEWAPIWG_API_KEY: "sk-env",
env: {
NEWAPIWG_BASE_URL: "https://env-gateway.example/v1/",
GEMINI_API_KEY: "gemini-env",
},
newApiWGApiKey: "sk-user",
});
expect(config).toEqual({
provider: "newapiwg",
apiKey: "sk-env",
apiKey: "sk-user",
baseUrl: "https://env-gateway.example/v1",
model: DEFAULT_NEWAPIWG_LLM_MODEL_ID,
});
@ -40,7 +44,7 @@ describe("quickstart model client", () => {
it("falls back to Gemini when NewApiWG is not configured", () => {
const config = resolveQuickstartModelConfig(new Headers(), {
GEMINI_API_KEY: "gemini-env",
env: { GEMINI_API_KEY: "gemini-env" },
});
expect(config).toEqual({

55
src/store/__tests__/userStore.test.ts

@ -2,7 +2,9 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { openLoginRequiredModal } from "@/utils/loginRequiredModal";
import {
USER_INFO_ENDPOINT,
USER_API_KEY_ENDPOINT,
USER_SESSION_STORAGE_KEY,
fetchGatewayApiKeyByToken,
fetchUserInfoByToken,
useUserStore,
} from "../userStore";
@ -127,6 +129,33 @@ describe("fetchUserInfoByToken", () => {
});
});
describe("fetchGatewayApiKeyByToken", () => {
it("fetches the current user gateway API key through the Next.js API route", async () => {
const fetcher = vi.fn().mockResolvedValue(
new Response(
JSON.stringify({
data: { apikey: "sk-user-gateway" },
message: "ok",
status: "0000",
}),
{ status: 200 }
)
);
await expect(fetchGatewayApiKeyByToken("login-token", fetcher)).resolves.toBe(
"sk-user-gateway"
);
expect(fetcher).toHaveBeenCalledWith(USER_API_KEY_ENDPOINT, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: "Bearer login-token",
token: "login-token",
},
});
});
});
describe("useUserStore.fetchUserInfo", () => {
afterEach(() => {
vi.mocked(openLoginRequiredModal).mockClear();
@ -135,8 +164,11 @@ describe("useUserStore.fetchUserInfo", () => {
useUserStore.setState({
token: null,
userInfo: null,
gatewayApiKey: null,
isLoadingUserInfo: false,
isLoadingGatewayApiKey: false,
userInfoError: null,
gatewayApiKeyError: null,
});
});
@ -152,7 +184,9 @@ describe("useUserStore.fetchUserInfo", () => {
})
);
const fetcher = vi.fn().mockResolvedValue(
const fetcher = vi
.fn()
.mockResolvedValueOnce(
new Response(
JSON.stringify({
data: {
@ -164,6 +198,16 @@ describe("useUserStore.fetchUserInfo", () => {
}),
{ status: 200 }
)
)
.mockResolvedValueOnce(
new Response(
JSON.stringify({
data: { apikey: "stored-gateway-key" },
message: "ok",
status: "0000",
}),
{ status: 200 }
)
);
vi.stubGlobal("fetch", fetcher);
@ -178,6 +222,15 @@ describe("useUserStore.fetchUserInfo", () => {
token: "stored-token",
},
});
expect(fetcher).toHaveBeenCalledWith(USER_API_KEY_ENDPOINT, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: "Bearer stored-token",
token: "stored-token",
},
});
expect(useUserStore.getState().gatewayApiKey).toBe("stored-gateway-key");
expect(openLoginRequiredModal).not.toHaveBeenCalled();
});

83
src/store/userStore.ts

@ -1,6 +1,7 @@
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import type {
UserApiKeyResponse,
UserInfo,
UserInfoResponse,
UserSession,
@ -10,6 +11,7 @@ import { openLoginRequiredModal } from "@/utils/loginRequiredModal";
export const USER_SESSION_STORAGE_KEY = "node-banana-user-session";
export const USER_INFO_ENDPOINT = "/api/user/info";
export const USER_API_KEY_ENDPOINT = "/api/user/apikey";
const userInfoRequests = new Map<string, Promise<UserSession>>();
@ -95,24 +97,98 @@ export const fetchUserInfoByToken = async (
return request;
};
export const fetchGatewayApiKeyByToken = async (
token: string,
fetcher: typeof fetch = fetch,
): Promise<string | null> => {
const response = await fetcher(USER_API_KEY_ENDPOINT, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
token,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch gateway API key: ${response.status}`);
}
const payload = (await response.json()) as UserApiKeyResponse;
if (payload.status !== "0000") {
throw new Error(
typeof payload.message === "string"
? payload.message
: "Failed to fetch gateway API key"
);
}
const apiKey = payload.data?.apikey;
return typeof apiKey === "string" && apiKey.trim() ? apiKey.trim() : null;
};
export const useUserStore = create<UserStoreState>()(
persist(
(set, get) => ({
token: null,
userInfo: null,
gatewayApiKey: null,
isLoadingUserInfo: false,
isLoadingGatewayApiKey: false,
userInfoError: null,
gatewayApiKeyError: null,
setToken: (token) => set({ token }),
fetchGatewayApiKey: async () => {
const token = get().token?.trim() || getStoredLoginToken();
if (!token) {
set({ gatewayApiKey: null, gatewayApiKeyError: null });
return null;
}
set({ isLoadingGatewayApiKey: true, gatewayApiKeyError: null });
try {
const apiKey = await fetchGatewayApiKeyByToken(token);
set({
gatewayApiKey: apiKey,
isLoadingGatewayApiKey: false,
gatewayApiKeyError: null,
});
return apiKey;
} catch (error) {
set({
gatewayApiKey: null,
isLoadingGatewayApiKey: false,
gatewayApiKeyError: getErrorMessage(error),
});
return null;
}
},
clearGatewayApiKey: () =>
set({
gatewayApiKey: null,
isLoadingGatewayApiKey: false,
gatewayApiKeyError: null,
}),
fetchUserInfo: async (token) => {
const normalizedToken = resolveLoginToken(token, get().token);
if (!normalizedToken) {
set({ token: null, userInfo: null, userInfoError: null });
set({
token: null,
userInfo: null,
gatewayApiKey: null,
userInfoError: null,
gatewayApiKeyError: null,
});
openLoginRequiredModal();
return null;
}
const current = get();
if (current.token === normalizedToken && current.userInfo) {
if (!current.gatewayApiKey) {
await get().fetchGatewayApiKey();
}
return current.userInfo;
}
@ -130,11 +206,13 @@ export const useUserStore = create<UserStoreState>()(
isLoadingUserInfo: false,
userInfoError: null,
});
await get().fetchGatewayApiKey();
return session.userInfo;
} catch (error) {
set({
isLoadingUserInfo: false,
userInfo: null,
gatewayApiKey: null,
userInfoError: getErrorMessage(error),
});
return null;
@ -144,8 +222,11 @@ export const useUserStore = create<UserStoreState>()(
set({
token: null,
userInfo: null,
gatewayApiKey: null,
isLoadingUserInfo: false,
isLoadingGatewayApiKey: false,
userInfoError: null,
gatewayApiKeyError: null,
}),
}),
{

4
src/store/utils/buildApiHeaders.ts

@ -34,15 +34,15 @@ export function buildGenerateHeaders(
const providerKey = provider as ProviderType;
const headerName = PROVIDER_HEADER_MAP[providerKey];
if (headerName) {
const config = providerSettings.providers[providerKey];
if (headerName) {
if (config?.apiKey) {
headers[headerName] = config.apiKey;
}
}
if (providerKey === "newapiwg" && config?.baseUrl) {
headers["X-NewApiWG-Base-URL"] = config.baseUrl;
}
}
return headers;
}

13
src/types/user.ts

@ -47,12 +47,25 @@ export interface UserSession {
userInfo: UserInfo;
}
export interface UserApiKeyResponse {
data?: {
apikey?: unknown;
} | null;
message?: unknown;
status?: unknown;
}
export interface UserStoreState {
token: string | null;
userInfo: UserInfo | null;
gatewayApiKey: string | null;
isLoadingUserInfo: boolean;
isLoadingGatewayApiKey: boolean;
userInfoError: string | null;
gatewayApiKeyError: string | null;
setToken: (token: string | null) => void;
fetchUserInfo: (token: string) => Promise<UserInfo | null>;
fetchGatewayApiKey: () => Promise<string | null>;
clearGatewayApiKey: () => void;
clearSession: () => void;
}

7
src/utils/__tests__/authFetch.test.ts

@ -11,11 +11,11 @@ describe("auth fetch interceptor", () => {
afterEach(() => {
vi.mocked(openLoginRequiredModal).mockClear();
vi.unstubAllGlobals();
useUserStore.setState({ token: null, userInfo: null });
useUserStore.setState({ token: null, userInfo: null, gatewayApiKey: null });
});
it("adds login headers to same-origin API requests", async () => {
useUserStore.setState({ token: "login-token" });
useUserStore.setState({ token: "login-token", gatewayApiKey: "gateway-key" });
const fetchMock = vi.fn(async () => new Response(JSON.stringify({ ok: true }), { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
@ -30,11 +30,12 @@ describe("auth fetch interceptor", () => {
const headers = new Headers(init.headers);
expect(headers.get("Authorization")).toBe("Bearer login-token");
expect(headers.get("token")).toBe("login-token");
expect(headers.get("X-NewApiWG-Key")).toBe("gateway-key");
expect(init.credentials).toBe("same-origin");
});
it("does not attach login headers to external requests", async () => {
useUserStore.setState({ token: "login-token" });
useUserStore.setState({ token: "login-token", gatewayApiKey: "gateway-key" });
const fetchMock = vi.fn(async () => new Response(null, { status: 200 }));
vi.stubGlobal("fetch", fetchMock);

16
src/utils/authFetch.ts

@ -1,6 +1,7 @@
"use client";
import { useUserStore } from "@/store/userStore";
import { NEWAPIWG_API_KEY_HEADER } from "@/utils/gatewayHeaders";
import { openLoginRequiredModal } from "@/utils/loginRequiredModal";
const ORIGINAL_FETCH_KEY = Symbol.for("popiart.originalFetch");
@ -32,21 +33,32 @@ function getLoginToken(): string | null {
return token?.trim() || null;
}
function getGatewayApiKey(): string | null {
const apiKey = useUserStore.getState().gatewayApiKey;
return apiKey?.trim() || null;
}
function withLoginHeaders(input: RequestInfo | URL, init?: RequestInit): RequestInit | undefined {
if (!isSameOriginApiRequest(input)) {
return init;
}
const token = getLoginToken();
const gatewayApiKey = getGatewayApiKey();
const requestHeaders = input instanceof Request ? input.headers : undefined;
const headers = new Headers(init?.headers ?? requestHeaders);
if (gatewayApiKey) {
headers.set(NEWAPIWG_API_KEY_HEADER, gatewayApiKey);
}
if (!token) {
return {
...init,
headers,
credentials: init?.credentials ?? "same-origin",
};
}
const requestHeaders = input instanceof Request ? input.headers : undefined;
const headers = new Headers(init?.headers ?? requestHeaders);
headers.set("Authorization", `Bearer ${token}`);
headers.set("token", token);

2
src/utils/gatewayHeaders.ts

@ -0,0 +1,2 @@
export const NEWAPIWG_API_KEY_HEADER = "X-NewApiWG-Key";
export const LEGACY_NEWAPIWG_API_KEY_HEADER = "NEWAPIWG_API_KEY";
Loading…
Cancel
Save