Verwaiste AdGuard-Clients (magic_<deviceId> existiert, aber DB-Row fehlt nach Crash zwischen clients/add und DB-Upsert) führten beim Re-Register zu 400 → 502. Jetzt: bei 400 auf clients/update zurückfallen und den bestehenden Client auf die frisch generierte clientId umbiegen. Behebt Magic-Register-502. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
146 lines
4.3 KiB
TypeScript
146 lines
4.3 KiB
TypeScript
/**
|
|
* AdGuard Home API Client für RebreakMagic DNS-over-HTTPS Client-Provisioning.
|
|
* Docs: https://github.com/AdguardTeam/AdGuardHome/tree/master/openapi
|
|
*/
|
|
|
|
export interface AdGuardClientOptions {
|
|
use_global_settings?: boolean;
|
|
filtering_enabled?: boolean;
|
|
parental_enabled?: boolean;
|
|
safebrowsing_enabled?: boolean;
|
|
safesearch_enabled?: boolean;
|
|
blocked_services?: string[];
|
|
upstreams?: string[];
|
|
tags?: string[];
|
|
}
|
|
|
|
interface AdGuardClientPayload {
|
|
name: string;
|
|
ids: string[];
|
|
use_global_settings?: boolean;
|
|
filtering_enabled?: boolean;
|
|
parental_enabled?: boolean;
|
|
safebrowsing_enabled?: boolean;
|
|
safesearch_enabled?: boolean;
|
|
blocked_services?: string[];
|
|
upstreams?: string[];
|
|
tags?: string[];
|
|
}
|
|
|
|
/**
|
|
* Erstellt einen AdGuard Persistent Client mit gegebener Client-ID (DNS-Token).
|
|
* AdGuard nutzt die Client-ID im DoH-URL-Path: /dns-query/{clientId}
|
|
*
|
|
* @param name - Interner Client-Name (z.B. "magic_<deviceId>")
|
|
* @param clientId - DNS-Token (wird in DoH URL embedded)
|
|
* @param options - Filtering/Blocking-Optionen
|
|
*/
|
|
export async function createAdGuardClient(
|
|
name: string,
|
|
clientId: string,
|
|
options: AdGuardClientOptions = {},
|
|
): Promise<void> {
|
|
const config = useRuntimeConfig();
|
|
const baseUrl = config.adguardBaseUrl || "https://dns.rebreak.org";
|
|
const user = config.adguardUser;
|
|
const password = config.adguardPassword;
|
|
|
|
if (!user || !password) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
message: "ADGUARD_USER and ADGUARD_PASSWORD required for Magic features",
|
|
});
|
|
}
|
|
|
|
const payload: AdGuardClientPayload = {
|
|
name,
|
|
ids: [clientId],
|
|
...options,
|
|
};
|
|
|
|
const authHeader = `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`;
|
|
const headers = {
|
|
Authorization: authHeader,
|
|
"Content-Type": "application/json",
|
|
};
|
|
|
|
try {
|
|
const response = await $fetch(`${baseUrl}/control/clients/add`, {
|
|
method: "POST",
|
|
headers,
|
|
body: payload,
|
|
});
|
|
return response as void;
|
|
} catch (err: any) {
|
|
// AdGuard antwortet mit 400, wenn schon ein Client mit diesem Namen ODER
|
|
// dieser Client-ID existiert. Das passiert bei Re-Registrierung wenn ein
|
|
// früherer Versuch den Client anlegte, aber die DB-Row nie geschrieben wurde
|
|
// (z.B. Prozess-Crash zwischen clients/add und DB-Upsert). Statt hart zu
|
|
// failen → idempotent auf clients/update zurückfallen und den bestehenden
|
|
// Client auf den frisch generierten clientId umbiegen.
|
|
const status = err?.status ?? err?.response?.status ?? err?.statusCode;
|
|
if (status === 400) {
|
|
try {
|
|
const response = await $fetch(`${baseUrl}/control/clients/update`, {
|
|
method: "POST",
|
|
headers,
|
|
body: { name, data: payload },
|
|
});
|
|
return response as void;
|
|
} catch (updErr: any) {
|
|
console.error(
|
|
"[AdGuard] Client update (after add-conflict) failed:",
|
|
updErr,
|
|
);
|
|
throw createError({
|
|
statusCode: 502,
|
|
message: `AdGuard API error (update): ${updErr.message || "unknown"}`,
|
|
});
|
|
}
|
|
}
|
|
console.error("[AdGuard] Client creation failed:", err);
|
|
throw createError({
|
|
statusCode: 502,
|
|
message: `AdGuard API error: ${err.message || "unknown"}`,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Löscht einen AdGuard Persistent Client.
|
|
* @param name - Interner Client-Name (z.B. "magic_<deviceId>")
|
|
*/
|
|
export async function deleteAdGuardClient(name: string): Promise<void> {
|
|
const config = useRuntimeConfig();
|
|
const baseUrl = config.adguardBaseUrl || "https://dns.rebreak.org";
|
|
const user = config.adguardUser;
|
|
const password = config.adguardPassword;
|
|
|
|
if (!user || !password) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
message: "ADGUARD_USER and ADGUARD_PASSWORD required for Magic features",
|
|
});
|
|
}
|
|
|
|
const authHeader = `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`;
|
|
|
|
try {
|
|
const response = await $fetch(`${baseUrl}/control/clients/delete`, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: authHeader,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: { name },
|
|
});
|
|
return response as void;
|
|
} catch (err: any) {
|
|
console.error("[AdGuard] Client deletion failed:", err);
|
|
throw createError({
|
|
statusCode: 502,
|
|
message: `AdGuard API error: ${err.message || "unknown"}`,
|
|
});
|
|
}
|
|
}
|