fix(control-plane): pass API key to dataplane for tenant auth (#243)

The control plane proxy routes never sent an Authorization header to
the dataplane API. With the tenant extension active, all GUI requests
failed with "Invalid API key".

Add HINDSIGHT_CP_DATAPLANE_API_KEY env var support to hindsight-client.ts
and propagate auth headers to both SDK clients and all direct fetch routes.
This commit is contained in:
Anton Evseev 2026-01-30 18:06:00 +10:00 committed by GitHub
parent ff20bf9dc7
commit 03bf13e9e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 47 additions and 32 deletions

View file

@ -1,6 +1,5 @@
import { NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function GET(
request: Request,
@ -15,7 +14,7 @@ export async function GET(
const response = await fetch(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/directives/${directiveId}`,
{ method: "GET" }
{ method: "GET", headers: getDataplaneHeaders() }
);
if (!response.ok) {
@ -49,7 +48,7 @@ export async function PATCH(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/directives/${directiveId}`,
{
method: "PATCH",
headers: { "Content-Type": "application/json" },
headers: getDataplaneHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify(body),
}
);
@ -84,7 +83,7 @@ export async function DELETE(
const response = await fetch(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/directives/${directiveId}`,
{ method: "DELETE" }
{ method: "DELETE", headers: getDataplaneHeaders() }
);
if (!response.ok) {

View file

@ -1,6 +1,5 @@
import { NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function GET(request: Request, { params }: { params: Promise<{ bankId: string }> }) {
try {
@ -22,7 +21,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ bank
}
const url = `${DATAPLANE_URL}/v1/default/banks/${bankId}/directives${queryParams.toString() ? `?${queryParams}` : ""}`;
const response = await fetch(url, { method: "GET" });
const response = await fetch(url, { method: "GET", headers: getDataplaneHeaders() });
if (!response.ok) {
const errorText = await response.text();
@ -50,7 +49,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ ban
const response = await fetch(`${DATAPLANE_URL}/v1/default/banks/${bankId}/directives`, {
method: "POST",
headers: { "Content-Type": "application/json" },
headers: getDataplaneHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify(body),
});

View file

@ -1,6 +1,5 @@
import { NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function POST(
request: Request,
@ -18,7 +17,7 @@ export async function POST(
const response = await fetch(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}/refresh`,
{ method: "POST" }
{ method: "POST", headers: getDataplaneHeaders() }
);
if (!response.ok) {

View file

@ -1,6 +1,5 @@
import { NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function GET(
request: Request,
@ -18,7 +17,7 @@ export async function GET(
const response = await fetch(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}`,
{ method: "GET" }
{ method: "GET", headers: getDataplaneHeaders() }
);
if (!response.ok) {
@ -58,7 +57,7 @@ export async function PATCH(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}`,
{
method: "PATCH",
headers: { "Content-Type": "application/json" },
headers: getDataplaneHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify(body),
}
);
@ -96,7 +95,7 @@ export async function DELETE(
const response = await fetch(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}`,
{ method: "DELETE" }
{ method: "DELETE", headers: getDataplaneHeaders() }
);
if (!response.ok) {

View file

@ -1,6 +1,5 @@
import { NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function GET(request: Request, { params }: { params: Promise<{ bankId: string }> }) {
try {
@ -22,7 +21,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ bank
}
const url = `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models${queryParams.toString() ? `?${queryParams}` : ""}`;
const response = await fetch(url, { method: "GET" });
const response = await fetch(url, { method: "GET", headers: getDataplaneHeaders() });
if (!response.ok) {
const errorText = await response.text();
@ -53,7 +52,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ ban
const response = await fetch(`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models`, {
method: "POST",
headers: { "Content-Type": "application/json" },
headers: getDataplaneHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify(body),
});

View file

@ -1,6 +1,5 @@
import { NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function GET(
request: Request,
@ -15,7 +14,7 @@ export async function GET(
const response = await fetch(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${modelId}`,
{ method: "GET" }
{ method: "GET", headers: getDataplaneHeaders() }
);
if (!response.ok) {

View file

@ -1,5 +1,6 @@
import { NextResponse } from "next/server";
import { createClient, createConfig, sdk } from "@vectorize-io/hindsight-client";
import { getDataplaneHeaders } from "@/lib/hindsight-client";
const HEALTH_CHECK_TIMEOUT_MS = 3000;
@ -27,6 +28,7 @@ export async function GET() {
createConfig({
baseUrl: dataplaneUrl,
signal: controller.signal,
headers: getDataplaneHeaders(),
})
);

View file

@ -1,6 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client";
export async function GET(
request: NextRequest,
@ -19,9 +18,7 @@ export async function GET(
`${DATAPLANE_URL}/v1/default/banks/${bankId}/memories/${memoryId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
headers: getDataplaneHeaders({ "Content-Type": "application/json" }),
}
);

View file

@ -5,17 +5,39 @@
import { HindsightClient, createClient, createConfig, sdk } from "@vectorize-io/hindsight-client";
const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
export const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
const DATAPLANE_API_KEY = process.env.HINDSIGHT_CP_DATAPLANE_API_KEY || "";
/**
* Auth headers for direct fetch calls to the dataplane API.
*/
export function getDataplaneHeaders(extra?: Record<string, string>): Record<string, string> {
const headers: Record<string, string> = { ...extra };
if (DATAPLANE_API_KEY) {
headers["Authorization"] = `Bearer ${DATAPLANE_API_KEY}`;
}
return headers;
}
/**
* High-level client with convenience methods
*/
export const hindsightClient = new HindsightClient({ baseUrl: DATAPLANE_URL });
export const hindsightClient = new HindsightClient({
baseUrl: DATAPLANE_URL,
apiKey: DATAPLANE_API_KEY || undefined,
});
/**
* Low-level client for direct SDK access
*/
export const lowLevelClient = createClient(createConfig({ baseUrl: DATAPLANE_URL }));
export const lowLevelClient = createClient(
createConfig({
baseUrl: DATAPLANE_URL,
headers: DATAPLANE_API_KEY
? { Authorization: `Bearer ${DATAPLANE_API_KEY}` }
: undefined,
}),
);
/**
* Export SDK functions for direct API access