chahinebrini c1edef8abd feat(magic): RebreakMagic device-binding + DNS profile
- backend: /api/magic/{register,devices,profile,release} + AdGuard provisioning + 24h cooldown
- prisma: magic_binding_fields migration (additive on UserDevice)
- mac-app: Phase 2 - Login + MacRegistration + Profile install
- marketing: landing section + /download/rebreakmagic + DMG
- lyra: forbidden phrases + RebreakMagic coach guidance
2026-06-02 09:15:19 +02:00

119 lines
3.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')}`;
try {
const response = await $fetch(`${baseUrl}/control/clients/add`, {
method: 'POST',
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json',
},
body: payload,
});
return response as void;
} catch (err: any) {
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'}`,
});
}
}