feat(pi-hole): support no-auth (#4480)
This commit is contained in:
@@ -145,7 +145,7 @@ export const integrationDefs = {
|
|||||||
},
|
},
|
||||||
piHole: {
|
piHole: {
|
||||||
name: "Pi-hole",
|
name: "Pi-hole",
|
||||||
secretKinds: [["apiKey"]],
|
secretKinds: [["apiKey"], []],
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/pi-hole.svg",
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/pi-hole.svg",
|
||||||
category: ["dnsHole"],
|
category: ["dnsHole"],
|
||||||
documentationUrl: createDocumentationLink("/docs/integrations/pi-hole"),
|
documentationUrl: createDocumentationLink("/docs/integrations/pi-hole"),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { dnsBlockingGetSchema, sessionResponseSchema, statsSummaryGetSchema } fr
|
|||||||
const localLogger = logger.child({ module: "PiHoleIntegrationV6" });
|
const localLogger = logger.child({ module: "PiHoleIntegrationV6" });
|
||||||
|
|
||||||
export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIntegration {
|
export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIntegration {
|
||||||
private readonly sessionStore: SessionStore<string>;
|
private readonly sessionStore: SessionStore<{ sid: string | null }>;
|
||||||
|
|
||||||
constructor(integration: IntegrationInput) {
|
constructor(integration: IntegrationInput) {
|
||||||
super(integration);
|
super(integration);
|
||||||
@@ -28,7 +28,7 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
const response = await this.withAuthAsync(async (sessionId) => {
|
const response = await this.withAuthAsync(async (sessionId) => {
|
||||||
return await fetchWithTrustedCertificatesAsync(this.url("/api/dns/blocking"), {
|
return await fetchWithTrustedCertificatesAsync(this.url("/api/dns/blocking"), {
|
||||||
headers: {
|
headers: {
|
||||||
sid: sessionId,
|
sid: sessionId ?? undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -46,7 +46,7 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
const response = await this.withAuthAsync(async (sessionId) => {
|
const response = await this.withAuthAsync(async (sessionId) => {
|
||||||
return fetchWithTrustedCertificatesAsync(this.url("/api/stats/summary"), {
|
return fetchWithTrustedCertificatesAsync(this.url("/api/stats/summary"), {
|
||||||
headers: {
|
headers: {
|
||||||
sid: sessionId,
|
sid: sessionId ?? undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -85,7 +85,7 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
const response = await this.withAuthAsync(async (sessionId) => {
|
const response = await this.withAuthAsync(async (sessionId) => {
|
||||||
return await fetchWithTrustedCertificatesAsync(this.url("/api/dns/blocking"), {
|
return await fetchWithTrustedCertificatesAsync(this.url("/api/dns/blocking"), {
|
||||||
headers: {
|
headers: {
|
||||||
sid: sessionId,
|
sid: sessionId ?? undefined,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ blocking: true }),
|
body: JSON.stringify({ blocking: true }),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -101,7 +101,7 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
const response = await this.withAuthAsync(async (sessionId) => {
|
const response = await this.withAuthAsync(async (sessionId) => {
|
||||||
return await fetchWithTrustedCertificatesAsync(this.url("/api/dns/blocking"), {
|
return await fetchWithTrustedCertificatesAsync(this.url("/api/dns/blocking"), {
|
||||||
headers: {
|
headers: {
|
||||||
sid: sessionId,
|
sid: sessionId ?? undefined,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ blocking: false, timer: duration }),
|
body: JSON.stringify({ blocking: false, timer: duration }),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -118,12 +118,16 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
* @param callback
|
* @param callback
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
private async withAuthAsync(callback: (sessionId: string) => Promise<UndiciResponse>) {
|
private async withAuthAsync(callback: (sessionId: string | null) => Promise<UndiciResponse>) {
|
||||||
|
if (!super.hasSecretValue("apiKey")) {
|
||||||
|
return await callback(null);
|
||||||
|
}
|
||||||
|
|
||||||
const storedSession = await this.sessionStore.getAsync();
|
const storedSession = await this.sessionStore.getAsync();
|
||||||
|
|
||||||
if (storedSession) {
|
if (storedSession) {
|
||||||
localLogger.debug("Using stored session for request", { integrationId: this.integration.id });
|
localLogger.debug("Using stored session for request", { integrationId: this.integration.id });
|
||||||
const response = await callback(storedSession);
|
const response = await callback(storedSession.sid);
|
||||||
if (response.status !== 401) {
|
if (response.status !== 401) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -132,7 +136,7 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sessionId = await this.getSessionAsync();
|
const sessionId = await this.getSessionAsync();
|
||||||
await this.sessionStore.setAsync(sessionId);
|
await this.sessionStore.setAsync({ sid: sessionId });
|
||||||
const response = await callback(sessionId);
|
const response = await callback(sessionId);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -141,11 +145,13 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
* Get a session id from the Pi-hole server
|
* Get a session id from the Pi-hole server
|
||||||
* @returns The session id
|
* @returns The session id
|
||||||
*/
|
*/
|
||||||
private async getSessionAsync(fetchAsync: typeof undiciFetch = fetchWithTrustedCertificatesAsync): Promise<string> {
|
private async getSessionAsync(
|
||||||
const apiKey = super.getSecretValue("apiKey");
|
fetchAsync: typeof undiciFetch = fetchWithTrustedCertificatesAsync,
|
||||||
|
): Promise<string | null> {
|
||||||
|
const apiKey = super.hasSecretValue("apiKey") ? super.getSecretValue("apiKey") : null;
|
||||||
const response = await fetchAsync(this.url("/api/auth"), {
|
const response = await fetchAsync(this.url("/api/auth"), {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ password: apiKey }),
|
body: JSON.stringify({ password: apiKey ?? "" }),
|
||||||
headers: {
|
headers: {
|
||||||
"User-Agent": "Homarr",
|
"User-Agent": "Homarr",
|
||||||
},
|
},
|
||||||
@@ -156,8 +162,13 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const result = await sessionResponseSchema.parseAsync(data);
|
const result = await sessionResponseSchema.parseAsync(data);
|
||||||
|
|
||||||
if (!result.session.sid) {
|
if (!result.session.valid) {
|
||||||
throw new ResponseError({ status: 401, url: response.url });
|
throw new ResponseError(
|
||||||
|
{ status: 401, url: response.url },
|
||||||
|
{
|
||||||
|
cause: result.session.message ? new Error(result.session.message) : undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
localLogger.info("Received session id successfully", { integrationId: this.integration.id });
|
localLogger.info("Received session id successfully", { integrationId: this.integration.id });
|
||||||
@@ -170,9 +181,14 @@ export class PiHoleIntegrationV6 extends Integration implements DnsHoleSummaryIn
|
|||||||
* @param sessionId The session id to remove
|
* @param sessionId The session id to remove
|
||||||
*/
|
*/
|
||||||
private async clearSessionAsync(
|
private async clearSessionAsync(
|
||||||
sessionId: string,
|
sessionId: string | null,
|
||||||
fetchAsync: typeof undiciFetch = fetchWithTrustedCertificatesAsync,
|
fetchAsync: typeof undiciFetch = fetchWithTrustedCertificatesAsync,
|
||||||
) {
|
) {
|
||||||
|
if (!sessionId) {
|
||||||
|
localLogger.debug("No session id to clear");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetchAsync(this.url("/api/auth"), {
|
const response = await fetchAsync(this.url("/api/auth"), {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { z } from "zod/v4";
|
|||||||
|
|
||||||
export const sessionResponseSchema = z.object({
|
export const sessionResponseSchema = z.object({
|
||||||
session: z.object({
|
session: z.object({
|
||||||
|
valid: z.boolean(),
|
||||||
sid: z.string().nullable(),
|
sid: z.string().nullable(),
|
||||||
message: z.string().nullable(),
|
message: z.string().nullable(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user