From 2e412b61a7f6bd464dbc0dde3908e76f8b89ec06 Mon Sep 17 00:00:00 2001 From: Hendrik Date: Fri, 1 May 2026 18:41:38 +0200 Subject: [PATCH] =?UTF-8?q?v0.1.4=20=E2=80=94=20MaxMind=20Basic=20Auth=20+?= =?UTF-8?q?=20Account-ID=20Field,=20detailed=20download=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(app)/settings/page.tsx | 53 +++++++++++++++++++++++---------- app/api/settings/geo/route.ts | 56 ++++++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 19 deletions(-) diff --git a/app/(app)/settings/page.tsx b/app/(app)/settings/page.tsx index 1ace520..ab1185a 100644 --- a/app/(app)/settings/page.tsx +++ b/app/(app)/settings/page.tsx @@ -29,6 +29,7 @@ export default function SettingsPage() { const [status, setStatus] = useState(null); const [geo, setGeo] = useState<{ available: boolean; path: string } | null>(null); const [licenseKey, setLicenseKey] = useState(""); + const [accountId, setAccountId] = useState(""); const [installingGeo, setInstallingGeo] = useState(false); const [geoMsg, setGeoMsg] = useState(""); const [saving, setSaving] = useState(false); @@ -56,15 +57,23 @@ export default function SettingsPage() { const r = await fetch("/api/settings/geo", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ license_key: licenseKey.trim() }), + body: JSON.stringify({ + license_key: licenseKey.trim(), + ...(accountId.trim() ? { account_id: accountId.trim() } : {}), + }), }); const d = await r.json(); if (r.ok) { setGeoMsg("GeoLite2-DB installiert."); setLicenseKey(""); + setAccountId(""); load(); } else { - setGeoMsg(`Fehler: ${d.error || "Download fehlgeschlagen"}`); + const parts = [`Fehler: ${d.error || "Download fehlgeschlagen"}`]; + if (d.status) parts.push(`HTTP ${d.status}`); + if (d.detail) parts.push(d.detail); + if (d.hint) parts.push(`→ ${d.hint}`); + setGeoMsg(parts.join(" — ")); } } finally { setInstallingGeo(false); @@ -240,23 +249,37 @@ export default function SettingsPage() { ) : ( -
- -
+
+
+ setLicenseKey(e.target.value)} + id="accountId" + type="text" + placeholder="123456" + value={accountId} + onChange={(e) => setAccountId(e.target.value)} disabled={installingGeo} /> - +

Empfohlen — neue License-Keys brauchen die Account-ID (Basic Auth).

+
+
+ +
+ setLicenseKey(e.target.value)} + disabled={installingGeo} + /> + +
+

Lädt GeoLite2-Country.mmdb herunter. EULA muss im MaxMind-Account akzeptiert sein.

-

Lädt GeoLite2-Country.mmdb herunter und aktiviert Geo-Lookup.

)} {geoMsg &&

{geoMsg}

} diff --git a/app/api/settings/geo/route.ts b/app/api/settings/geo/route.ts index b214974..11d267c 100644 --- a/app/api/settings/geo/route.ts +++ b/app/api/settings/geo/route.ts @@ -19,8 +19,25 @@ export async function GET() { const schema = z.object({ license_key: z.string().min(10), + account_id: z.string().optional(), }); +type DownloadResult = { ok: true; body: Buffer } | { ok: false; status: number; text: string }; + +async function tryDownload(url: string, headers: Record = {}): Promise { + const res = await fetch(url, { headers: { "User-Agent": "corex-nexredirect", ...headers } }); + if (!res.ok) { + const text = await res.text().catch(() => ""); + return { ok: false, status: res.status, text: text.slice(0, 500) }; + } + const ct = res.headers.get("content-type") || ""; + const buf = Buffer.from(await res.arrayBuffer()); + if (ct.includes("text/html") || buf.slice(0, 4).toString() === "; label: string }> = []; + + if (account_id) { + const auth = Buffer.from(`${account_id}:${license_key}`).toString("base64"); + attempts.push({ + url: "https://download.maxmind.com/geoip/databases/GeoLite2-Country/download?suffix=tar.gz", + headers: { Authorization: `Basic ${auth}` }, + label: "basic-auth", + }); + } + attempts.push({ + url: `https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${encodeURIComponent(license_key)}&suffix=tar.gz`, + label: "legacy", + }); + + let lastErr: { status: number; text: string; label: string } | null = null; + let buf: Buffer | null = null; + for (const a of attempts) { + const r = await tryDownload(a.url, a.headers); + if (r.ok) { buf = r.body; break; } + lastErr = { status: r.status, text: r.text, label: a.label }; + } + + if (!buf) { + return NextResponse.json({ + error: "download_failed", + detail: lastErr?.text || "no detail", + status: lastErr?.status, + hint: account_id + ? "MaxMind hat das Tarball nicht ausgeliefert. Stimmen Account-ID und License-Key? GeoLite2 muss im Account aktiviert sein und EULA akzeptiert." + : "Falls dein License-Key über das neue MaxMind-Account-System erstellt wurde, ist die Account-ID nötig (siehe Account → My License Keys oder unter Account-Details).", + }, { status: 502 }); + } try { - const res = await fetch(url); - if (!res.ok) return NextResponse.json({ error: "download_failed", status: res.status }, { status: 502 }); - const buf = Buffer.from(await res.arrayBuffer()); const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "geo-")); const tarPath = path.join(tmpDir, "geo.tgz"); await fs.writeFile(tarPath, buf);