Add license auth header to Miyo requests (#2260)

* Add license auth header to Miyo requests

* Remove unused self-host API key setting

* Keep self-host API key in settings

* Log Miyo auth header presence
This commit is contained in:
Wenzheng Jiang 2026-03-05 07:37:51 +09:00 committed by GitHub
parent adbb646729
commit f51036fa8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 9 deletions

View file

@ -1,3 +1,4 @@
import { logInfo } from "@/logger";
import { MiyoClient } from "@/miyo/MiyoClient";
import { MiyoServiceDiscovery } from "@/miyo/MiyoServiceDiscovery";
import { getSettings } from "@/settings/model";
@ -31,11 +32,12 @@ describe("MiyoClient.parseDoc", () => {
const mockedRequestUrl = requestUrl as jest.MockedFunction<typeof requestUrl>;
const mockedGetSettings = getSettings as jest.MockedFunction<typeof getSettings>;
const mockedGetInstance = MiyoServiceDiscovery.getInstance as unknown as jest.Mock;
const mockedLogInfo = logInfo as jest.MockedFunction<typeof logInfo>;
beforeEach(() => {
jest.clearAllMocks();
mockedGetSettings.mockReturnValue({
selfHostApiKey: "",
plusLicenseKey: "plus-test-license",
debug: false,
} as any);
mockResolveBaseUrl.mockResolvedValue("http://127.0.0.1:8742");
@ -71,10 +73,21 @@ describe("MiyoClient.parseDoc", () => {
expect.objectContaining({
url: "http://127.0.0.1:8742/v0/parse-doc",
method: "POST",
headers: {
Authorization: "Bearer plus-test-license",
},
contentType: "application/json",
body: JSON.stringify({ path: "/tmp/sample.pdf" }),
})
);
expect(mockedLogInfo).toHaveBeenCalledWith(
"Miyo request:",
expect.objectContaining({
method: "POST",
url: "http://127.0.0.1:8742/v0/parse-doc",
hasAuthorizationHeader: true,
})
);
});
it("throws when /v0/parse-doc returns an error status", async () => {

View file

@ -1,3 +1,4 @@
import { getDecryptedKey } from "@/encryptionService";
import { logError, logInfo, logWarn } from "@/logger";
import { getSettings } from "@/settings/model";
import { err2String } from "@/utils";
@ -360,18 +361,21 @@ export class MiyoClient {
/**
* Build request headers, including auth when configured.
* Both `Authorization: Bearer` and `X-API-Key` are sent for compatibility
* with different Miyo server configurations.
* `Authorization` uses the Copilot Plus license key.
*
* @returns Headers object for requestUrl.
*/
private buildHeaders(): Record<string, string> {
const apiKey = getSettings().selfHostApiKey;
private async buildHeaders(): Promise<Record<string, string>> {
const settings = getSettings();
const headers: Record<string, string> = {};
if (apiKey) {
headers.Authorization = `Bearer ${apiKey}`;
headers["X-API-Key"] = apiKey;
const licenseKey = settings.plusLicenseKey
? await getDecryptedKey(settings.plusLicenseKey)
: "";
if (licenseKey) {
headers.Authorization = `Bearer ${licenseKey}`;
}
return headers;
}
@ -402,10 +406,12 @@ export class MiyoClient {
}
const body = options.body ? JSON.stringify(options.body) : undefined;
const headers = await this.buildHeaders();
logInfo("Miyo request:", {
method: options.method,
url: url.toString(),
hasBody: Boolean(body),
hasAuthorizationHeader: Boolean(headers.Authorization),
...(getSettings().debug && options.method === "POST" ? { postBody: options.body } : {}),
});
@ -415,7 +421,7 @@ export class MiyoClient {
const response = await requestUrl({
url: url.toString(),
method: options.method,
headers: this.buildHeaders(),
headers,
contentType: body ? "application/json" : undefined,
body,
throw: false,