feat: verschlüsselte Integrationseinstellungen, Haushaltsmitglieder mit Ersteller, Backup-Download
This commit is contained in:
1 parent
a1195223e0
commit
267f4fcf70
21 files changed
+724
-30
No files matched your search
+14
-10
@@ -1,3 +1,5 @@
|
||||
import { getLogoSettings } from "./integration-settings";
|
||||
|
||||
export type LogoCandidate = { name: string; domain: string; logoUrl: string | null };
|
||||
|
||||
const DOMAIN_RE = /^[a-z0-9.-]+\.[a-z]{2,}$/i;
|
||||
@@ -10,11 +12,11 @@ export function normalizeDomain(domain: string | null | undefined): string | nul
|
||||
}
|
||||
|
||||
/** Baut die öffentliche Logo-URL für eine gespeicherte Domain. Key bleibt serverseitig. */
|
||||
export function logoImageUrl(domain: string | null | undefined): string | null {
|
||||
export async function logoImageUrl(domain: string | null | undefined): Promise<string | null> {
|
||||
const d = normalizeDomain(domain);
|
||||
const token = process.env.LOGO_DEV_PUBLISHABLE_KEY;
|
||||
if (!d || !token) return null;
|
||||
return `https://img.logo.dev/${d}?token=${token}&size=128&format=png`;
|
||||
const { publishableKey } = await getLogoSettings();
|
||||
if (!d || !publishableKey) return null;
|
||||
return `https://img.logo.dev/${d}?token=${publishableKey}&size=128&format=png`;
|
||||
}
|
||||
|
||||
/** Kürzel aus dem Anbieternamen als Fallback, z. B. "Therme Erding" → "TE". */
|
||||
@@ -26,18 +28,20 @@ export function monogram(name: string): string {
|
||||
}
|
||||
|
||||
/** Reichert ein Voucher-Objekt um die öffentliche logoUrl an (serverseitig). */
|
||||
export function withLogoUrl<T extends { providerDomain: string | null }>(v: T): T & { logoUrl: string | null } {
|
||||
return { ...v, logoUrl: logoImageUrl(v.providerDomain) };
|
||||
export async function withLogoUrl<T extends { providerDomain: string | null }>(
|
||||
v: T
|
||||
): Promise<T & { logoUrl: string | null }> {
|
||||
return { ...v, logoUrl: await logoImageUrl(v.providerDomain) };
|
||||
}
|
||||
|
||||
/** Sucht Anbieter über Logo.dev (serverseitig, Secret-Key). Gibt max. 8 Treffer zurück. */
|
||||
export async function searchLogos(query: string): Promise<LogoCandidate[]> {
|
||||
const key = process.env.LOGO_DEV_SECRET_KEY;
|
||||
if (!key) throw new Error("LOGO_DEV_SECRET_KEY fehlt");
|
||||
const { secretKey } = await getLogoSettings();
|
||||
if (!secretKey) throw new Error("LOGO_DEV_SECRET_KEY fehlt");
|
||||
const q = query.trim();
|
||||
if (!q) return [];
|
||||
const res = await fetch(`https://api.logo.dev/search?q=${encodeURIComponent(q)}`, {
|
||||
headers: { Authorization: `Bearer ${key}` },
|
||||
headers: { Authorization: `Bearer ${secretKey}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`Logosuche ${res.status}`);
|
||||
const data: unknown = await res.json();
|
||||
@@ -49,7 +53,7 @@ export async function searchLogos(query: string): Promise<LogoCandidate[]> {
|
||||
const domain = normalizeDomain(typeof rec.domain === "string" ? rec.domain : null);
|
||||
if (!domain) continue;
|
||||
const name = typeof rec.name === "string" && rec.name.trim() ? rec.name.trim() : domain;
|
||||
out.push({ name, domain, logoUrl: logoImageUrl(domain) });
|
||||
out.push({ name, domain, logoUrl: await logoImageUrl(domain) });
|
||||
if (out.length >= 8) break;
|
||||
}
|
||||
return out;
|
||||
|
||||
Reference in new issue
Block a user