From a2a7d2ce7f4d75a50cf8b669672b67bb9c920ab0 Mon Sep 17 00:00:00 2001 From: itsmeakhil Date: Sat, 9 May 2026 15:32:57 +0530 Subject: [PATCH 1/2] UI UX --- apps/backend/app/api/router.py | 2 + .../app/api/routes/dns_lookup/__init__.py | 0 apps/backend/app/api/routes/dns_lookup/api.py | 17 + .../app/api/routes/dns_lookup/schema.py | 16 + .../app/api/routes/dns_lookup/services.py | 105 +++++ apps/web/messages/af.json | 8 + apps/web/messages/ar.json | 8 + apps/web/messages/ca.json | 8 + apps/web/messages/cs.json | 8 + apps/web/messages/da.json | 8 + apps/web/messages/de.json | 8 + apps/web/messages/el.json | 8 + apps/web/messages/en.json | 22 + apps/web/messages/es.json | 8 + apps/web/messages/fa.json | 8 + apps/web/messages/fr.json | 8 + apps/web/messages/id.json | 8 + apps/web/messages/it.json | 8 + apps/web/messages/ja.json | 8 + apps/web/messages/ko.json | 8 + apps/web/messages/ms.json | 8 + apps/web/messages/nb.json | 8 + apps/web/messages/nl.json | 8 + apps/web/messages/pl.json | 8 + apps/web/messages/pt-BR.json | 8 + apps/web/messages/pt.json | 8 + apps/web/messages/ru.json | 8 + apps/web/messages/sv.json | 8 + apps/web/messages/tr.json | 8 + apps/web/messages/uk.json | 8 + apps/web/messages/vi.json | 8 + apps/web/messages/zh.json | 8 + apps/web/src/app/app/dns-lookup/page.tsx | 12 + .../dns-lookup/dns-lookup-layout.tsx | 410 ++++++++++++++++++ .../components/sidebar/data/sidebar-data.ts | 7 + apps/web/src/lib/metadata.ts | 6 + apps/web/src/lib/tab-registry.tsx | 1 + apps/web/src/lib/tool-i18n.ts | 1 + 38 files changed, 807 insertions(+) create mode 100644 apps/backend/app/api/routes/dns_lookup/__init__.py create mode 100644 apps/backend/app/api/routes/dns_lookup/api.py create mode 100644 apps/backend/app/api/routes/dns_lookup/schema.py create mode 100644 apps/backend/app/api/routes/dns_lookup/services.py create mode 100644 apps/web/src/app/app/dns-lookup/page.tsx create mode 100644 apps/web/src/components/dns-lookup/dns-lookup-layout.tsx diff --git a/apps/backend/app/api/router.py b/apps/backend/app/api/router.py index 5b6c013c..e6a6098e 100644 --- a/apps/backend/app/api/router.py +++ b/apps/backend/app/api/router.py @@ -20,6 +20,7 @@ from app.api.routes.feedback.api import router as feedback_router from app.api.routes.redis_commander.api import router as redis_commander_router from app.api.routes.url_shortener.api import router as url_shortener_router +from app.api.routes.dns_lookup.api import router as dns_lookup_router api_router = APIRouter() api_router.include_router(health_router) @@ -42,3 +43,4 @@ api_router.include_router(feedback_router) api_router.include_router(redis_commander_router) api_router.include_router(url_shortener_router) +api_router.include_router(dns_lookup_router) diff --git a/apps/backend/app/api/routes/dns_lookup/__init__.py b/apps/backend/app/api/routes/dns_lookup/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/backend/app/api/routes/dns_lookup/api.py b/apps/backend/app/api/routes/dns_lookup/api.py new file mode 100644 index 00000000..46a10795 --- /dev/null +++ b/apps/backend/app/api/routes/dns_lookup/api.py @@ -0,0 +1,17 @@ +from fastapi import APIRouter, Query + +from app.api.routes.dns_lookup import services as svc +from app.api.routes.dns_lookup.schema import DNSLookupResult + +router = APIRouter(prefix="/dns-lookup", tags=["dns-lookup"]) + + +@router.get("/lookup", response_model=DNSLookupResult, summary="Perform DNS lookup for a domain") +async def lookup( + domain: str = Query(..., description="Domain name to look up"), + record_types: list[str] = Query( + default=["A", "AAAA", "MX", "TXT", "NS", "CNAME"], + description="DNS record types to query", + ), +) -> DNSLookupResult: + return await svc.lookup_domain(domain, record_types) diff --git a/apps/backend/app/api/routes/dns_lookup/schema.py b/apps/backend/app/api/routes/dns_lookup/schema.py new file mode 100644 index 00000000..cfc7a483 --- /dev/null +++ b/apps/backend/app/api/routes/dns_lookup/schema.py @@ -0,0 +1,16 @@ +from typing import Any +from pydantic import BaseModel + + +class DNSRecord(BaseModel): + type: str + value: str + ttl: int | None = None + priority: int | None = None + extra: dict[str, Any] | None = None + + +class DNSLookupResult(BaseModel): + domain: str + records: dict[str, list[DNSRecord]] + errors: dict[str, str] = {} diff --git a/apps/backend/app/api/routes/dns_lookup/services.py b/apps/backend/app/api/routes/dns_lookup/services.py new file mode 100644 index 00000000..5b8159ac --- /dev/null +++ b/apps/backend/app/api/routes/dns_lookup/services.py @@ -0,0 +1,105 @@ +import asyncio +import re +from typing import Any + +import dns.asyncresolver +import dns.exception +import dns.rdatatype +import dns.resolver + +from app.api.routes.dns_lookup.schema import DNSLookupResult, DNSRecord + +VALID_RECORD_TYPES = {"A", "AAAA", "MX", "TXT", "NS", "CNAME", "SOA", "CAA", "PTR"} +_DOMAIN_RE = re.compile(r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$") + + +def _is_valid_domain(domain: str) -> bool: + return bool(_DOMAIN_RE.match(domain)) and len(domain) <= 253 + + +async def _resolve_type(resolver: dns.asyncresolver.Resolver, domain: str, rtype: str) -> tuple[list[DNSRecord], str | None]: + try: + answers = await resolver.resolve(domain, rtype, raise_on_no_answer=False) + if answers.rrset is None: + return [], None + ttl = answers.rrset.ttl + records: list[DNSRecord] = [] + for rdata in answers.rrset: + rec = _parse_rdata(rtype, rdata, ttl) + if rec: + records.append(rec) + return records, None + except dns.resolver.NXDOMAIN: + return [], "NXDOMAIN: Domain does not exist" + except dns.resolver.NoNameservers: + return [], "No nameservers available" + except dns.resolver.Timeout: + return [], "Query timed out" + except dns.exception.DNSException as exc: + return [], str(exc) + + +def _parse_rdata(rtype: str, rdata: Any, ttl: int) -> DNSRecord | None: + if rtype == "A": + return DNSRecord(type=rtype, value=str(rdata.address), ttl=ttl) + if rtype == "AAAA": + return DNSRecord(type=rtype, value=str(rdata.address), ttl=ttl) + if rtype == "MX": + return DNSRecord(type=rtype, value=str(rdata.exchange).rstrip("."), ttl=ttl, priority=rdata.preference) + if rtype == "TXT": + value = " ".join(part.decode("utf-8", errors="replace") for part in rdata.strings) + return DNSRecord(type=rtype, value=value, ttl=ttl) + if rtype == "NS": + return DNSRecord(type=rtype, value=str(rdata.target).rstrip("."), ttl=ttl) + if rtype == "CNAME": + return DNSRecord(type=rtype, value=str(rdata.target).rstrip("."), ttl=ttl) + if rtype == "SOA": + return DNSRecord( + type=rtype, + value=str(rdata.mname).rstrip("."), + ttl=ttl, + extra={ + "rname": str(rdata.rname).rstrip("."), + "serial": rdata.serial, + "refresh": rdata.refresh, + "retry": rdata.retry, + "expire": rdata.expire, + "minimum": rdata.minimum, + }, + ) + if rtype == "CAA": + tag = rdata.tag.decode("utf-8", errors="replace") if isinstance(rdata.tag, bytes) else rdata.tag + value = rdata.value.decode("utf-8", errors="replace") if isinstance(rdata.value, bytes) else rdata.value + return DNSRecord(type=rtype, value=f'{rdata.flags} {tag} "{value}"', ttl=ttl) + if rtype == "PTR": + return DNSRecord(type=rtype, value=str(rdata.target).rstrip("."), ttl=ttl) + return DNSRecord(type=rtype, value=str(rdata), ttl=ttl) + + +async def lookup_domain(domain: str, record_types: list[str]) -> DNSLookupResult: + domain = domain.strip().lower().rstrip(".") + if not _is_valid_domain(domain): + return DNSLookupResult(domain=domain, records={}, errors={"_": "Invalid domain name"}) + + requested = [rt.upper() for rt in record_types if rt.upper() in VALID_RECORD_TYPES] + if not requested: + requested = ["A", "AAAA", "MX", "TXT", "NS", "CNAME"] + + resolver = dns.asyncresolver.Resolver() + resolver.timeout = 5 + resolver.lifetime = 10 + + tasks = {rtype: _resolve_type(resolver, domain, rtype) for rtype in requested} + results = await asyncio.gather(*tasks.values()) + + records: dict[str, list[DNSRecord]] = {} + errors: dict[str, str] = {} + for rtype, (recs, err) in zip(tasks.keys(), results): + if err and rtype == requested[0] and "NXDOMAIN" in err: + return DNSLookupResult(domain=domain, records={}, errors={"_": err}) + if err: + errors[rtype] = err + else: + records[rtype] = recs + + return DNSLookupResult(domain=domain, records=records, errors=errors) diff --git a/apps/web/messages/af.json b/apps/web/messages/af.json index 5d767770..6323c61c 100644 --- a/apps/web/messages/af.json +++ b/apps/web/messages/af.json @@ -282,6 +282,10 @@ "title": "Sertifikaat / PEM-dekodeerder", "description": "Dekodeer X.509-sertifikate en CSRs: onderwerp, uitreiker, geldigheid, SANs." }, + "sshKeyGenerator": { + "title": "SSH / RSA-sleutelgenerator", + "description": "Genereer Ed25519- of RSA-sleutelpare in die blaaier. Laai af in OpenSSH- en PEM-formaat." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP-/subnetrekenaar", "description": "IPv4 en IPv6 CIDR: netmasker, reekse en adresse." }, + "dnsLookup": { + "title": "DNS-soektog", + "description": "Navraag DNS-rekords vir enige domein — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Hash-generator", "description": "MD5, SHA-1 en SHA-2 vir teks of lêers." diff --git a/apps/web/messages/ar.json b/apps/web/messages/ar.json index 67b788a4..4e39a39c 100644 --- a/apps/web/messages/ar.json +++ b/apps/web/messages/ar.json @@ -282,6 +282,10 @@ "title": "فك ترميز الشهادة / PEM", "description": "فك ترميز شهادات X.509 وطلبات CSR: الموضوع، المصدر، الصلاحية، وأسماء SAN." }, + "sshKeyGenerator": { + "title": "مولد مفاتيح SSH / RSA", + "description": "توليد أزواج مفاتيح Ed25519 أو RSA في المتصفح. التنزيل بتنسيقات OpenSSH و PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "حاسبة IP / الشبكة الفرعية", "description": "IPv4 وIPv6 CIDR: قناع الشبكة، النطاقات، وعدد العناوين." }, + "dnsLookup": { + "title": "بحث DNS", + "description": "استعلام عن سجلات DNS لأي نطاق — A، AAAA، MX، TXT، NS، CNAME." + }, "hashGenerator": { "title": "مُولّد الهاش", "description": "MD5 وSHA-1 وSHA-2 لنص أو ملفات." diff --git a/apps/web/messages/ca.json b/apps/web/messages/ca.json index efd1ad3d..4b0feb86 100644 --- a/apps/web/messages/ca.json +++ b/apps/web/messages/ca.json @@ -282,6 +282,10 @@ "title": "Descodificador de certificats / PEM", "description": "Descodifica certificats X.509 i CSR: subjecte, emissor, validesa i SAN." }, + "sshKeyGenerator": { + "title": "Generador de claus SSH / RSA", + "description": "Genereu parells de claus Ed25519 o RSA al navegador. Descarregueu en formats OpenSSH i PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Calculadora IP / subxarxa", "description": "IPv4 i IPv6 CIDR: màscara, intervals i recompte d’adreces." }, + "dnsLookup": { + "title": "Cerca DNS", + "description": "Consulta els registres DNS de qualsevol domini — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Generador de hash", "description": "MD5, SHA-1 i SHA-2 per a text o fitxers." diff --git a/apps/web/messages/cs.json b/apps/web/messages/cs.json index 5514a48b..d7a6b0dd 100644 --- a/apps/web/messages/cs.json +++ b/apps/web/messages/cs.json @@ -282,6 +282,10 @@ "title": "Dekodér certifikátů / PEM", "description": "Dekódujte certifikáty X.509 a CSR: subjekt, vydavatel, platnost a SAN." }, + "sshKeyGenerator": { + "title": "Generátor klíčů SSH / RSA", + "description": "Generujte páry klíčů Ed25519 nebo RSA v prohlížeči. Stáhněte ve formátech OpenSSH a PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Kalkulačka IP / podsítě", "description": "IPv4 a IPv6 CIDR: maska, rozsahy a počty adres." }, + "dnsLookup": { + "title": "Vyhledávání DNS", + "description": "Dotazujte DNS záznamy pro libovolnou doménu — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Generátor hashů", "description": "MD5, SHA-1 a SHA-2 pro text nebo soubory." diff --git a/apps/web/messages/da.json b/apps/web/messages/da.json index 44d1e53e..c2fc4f3e 100644 --- a/apps/web/messages/da.json +++ b/apps/web/messages/da.json @@ -282,6 +282,10 @@ "title": "Certifikat / PEM-afkoder", "description": "Afkod X.509-certifikater og CSR’er: emne, udsteder, gyldighed og SAN." }, + "sshKeyGenerator": { + "title": "SSH / RSA-nøglegenerator", + "description": "Generér Ed25519- eller RSA-nøglepar i browseren. Download i OpenSSH- og PEM-format." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP-/subnetberegner", "description": "IPv4 og IPv6 CIDR: netmaske, intervaller og adresser." }, + "dnsLookup": { + "title": "DNS-opslag", + "description": "Forespørg DNS-poster for ethvert domæne — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Hash-generator", "description": "MD5, SHA-1 og SHA-2 til tekst eller filer." diff --git a/apps/web/messages/de.json b/apps/web/messages/de.json index 0edfaa1c..a14b5b30 100644 --- a/apps/web/messages/de.json +++ b/apps/web/messages/de.json @@ -282,6 +282,10 @@ "title": "Zertifikat- / PEM-Decoder", "description": "X.509-Zertifikate und CSRs dekodieren: Antragsteller, Aussteller, Gültigkeit, SANs." }, + "sshKeyGenerator": { + "title": "SSH / RSA-Schlüsselgenerator", + "description": "Ed25519- oder RSA-Schlüsselpaare generieren. Private und öffentliche Schlüssel im OpenSSH- und PEM-Format herunterladen." + }, "jsonFormatter": { "title": "JSON-Formatierer", "description": "JSON formatieren, validieren und bearbeiten." @@ -382,6 +386,10 @@ "title": "IP-/Subnetz-Rechner", "description": "IPv4 und IPv6 CIDR: Netzmaske, Bereiche und Adressanzahl." }, + "dnsLookup": { + "title": "DNS-Abfrage", + "description": "DNS-Einträge für jede Domain abfragen — A, AAAA, MX, TXT, NS, CNAME, SOA, CAA." + }, "hashGenerator": { "title": "Hash-Generator", "description": "MD5, SHA-1 und SHA-2 für Text oder Dateien." diff --git a/apps/web/messages/el.json b/apps/web/messages/el.json index 62218bea..a3df6afb 100644 --- a/apps/web/messages/el.json +++ b/apps/web/messages/el.json @@ -282,6 +282,10 @@ "title": "Αποκωδικοποιητής πιστοποιητικού / PEM", "description": "Αποκωδικοποίηση πιστοποιητικών X.509 και CSR: θέμα, εκδότης, ισχύς, SAN." }, + "sshKeyGenerator": { + "title": "Γεννήτρια κλειδιών SSH / RSA", + "description": "Δημιουργία ζευγών κλειδιών Ed25519 ή RSA στο πρόγραμμα περιήγησης. Λήψη σε μορφές OpenSSH και PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Υπολογιστής IP / υποδικτύου", "description": "IPv4 και IPv6 CIDR: μάσκα, εύρη και πλήθος διευθύνσεων." }, + "dnsLookup": { + "title": "Αναζήτηση DNS", + "description": "Αναζητήστε εγγραφές DNS για οποιοδήποτε τομέα — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Γεννήτρια hash", "description": "MD5, SHA-1 και SHA-2 για κείμενο ή αρχεία." diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index c73c4ae3..6d020192 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -339,6 +339,10 @@ "title": "Certificate / PEM Decoder", "description": "Decode X.509 certificates and CSRs: subject, issuer, validity, SANs." }, + "sshKeyGenerator": { + "title": "SSH / RSA Key Generator", + "description": "Generate Ed25519 or RSA key pairs. Download private and public keys in OpenSSH and PEM formats." + }, "encryptionPlayground": { "title": "Encryption Playground", "description": "AES-GCM with a raw key or passphrase; JSON bundles, Web Crypto only in your browser." @@ -419,6 +423,10 @@ "title": "IP / Subnet Calculator", "description": "IPv4 and IPv6 CIDR: netmask, ranges, and address counts." }, + "dnsLookup": { + "title": "DNS Lookup", + "description": "Query DNS records for any domain — A, AAAA, MX, TXT, NS, CNAME, SOA, CAA, PTR." + }, "hashGenerator": { "title": "Hash Generator", "description": "MD5, SHA-1, and SHA-2 digests for text or files." @@ -3430,5 +3438,19 @@ "errors": { "generationFailed": "Key generation failed" } + }, + "DnsLookup": { + "title": "DNS Lookup", + "subtitle": "Query DNS records for any domain — A, AAAA, MX, TXT, NS, CNAME, SOA, CAA, PTR.", + "placeholder": "Enter domain (e.g. github.com)", + "lookupBtn": "Lookup", + "looking": "Looking up…", + "refreshBtn": "Refresh", + "recordTypesLabel": "Record types", + "allBtn": "All", + "tryLabel": "Try:", + "errors": { + "lookupFailed": "DNS lookup failed" + } } } diff --git a/apps/web/messages/es.json b/apps/web/messages/es.json index a8ed4f58..3b99bfda 100644 --- a/apps/web/messages/es.json +++ b/apps/web/messages/es.json @@ -282,6 +282,10 @@ "title": "Decodificador de certificados / PEM", "description": "Decodifica certificados X.509 y CSR: sujeto, emisor, validez y SAN." }, + "sshKeyGenerator": { + "title": "Generador de claves SSH / RSA", + "description": "Genera pares de claves Ed25519 o RSA. Descarga claves en formatos OpenSSH y PEM." + }, "jsonFormatter": { "title": "Formateador JSON", "description": "Formatea, valida y edita datos JSON." @@ -382,6 +386,10 @@ "title": "Calculadora IP / subred", "description": "IPv4 e IPv6 CIDR: máscara, rangos y recuento de direcciones." }, + "dnsLookup": { + "title": "Búsqueda DNS", + "description": "Consulta registros DNS de cualquier dominio — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Generador de hash", "description": "MD5, SHA-1 y SHA-2 para texto o archivos." diff --git a/apps/web/messages/fa.json b/apps/web/messages/fa.json index 74ec336f..4e28ce9f 100644 --- a/apps/web/messages/fa.json +++ b/apps/web/messages/fa.json @@ -282,6 +282,10 @@ "title": "رمزگشای گواهی / PEM", "description": "رمزگشایی گواهی‌های X.509 و CSR: موضوع، صادرکننده، اعتبار و SAN." }, + "sshKeyGenerator": { + "title": "مولد کلید SSH / RSA", + "description": "در مرورگر جفت کلید Ed25519 یا RSA تولید کنید. در قالب‌های OpenSSH و PEM دانلود کنید." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "ماشین‌حساب IP / زیرشبکه", "description": "IPv4 و IPv6 CIDR: ماسک، ناحیه، و تعداد آدرس." }, + "dnsLookup": { + "title": "جستجوی DNS", + "description": "رکوردهای DNS هر دامنه را جستجو کنید — A، AAAA، MX، TXT، NS، CNAME." + }, "hashGenerator": { "title": "تولیدکنندهٔ هش", "description": "MD5، SHA-1 و SHA-2 برای متن یا فایل." diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json index 4ddd111a..bb4e736b 100644 --- a/apps/web/messages/fr.json +++ b/apps/web/messages/fr.json @@ -282,6 +282,10 @@ "title": "Décodeur de certificat / PEM", "description": "Décodez les certificats X.509 et les CSR : sujet, émetteur, validité et SAN." }, + "sshKeyGenerator": { + "title": "Générateur de clés SSH / RSA", + "description": "Générez des paires de clés Ed25519 ou RSA. Téléchargez les clés en formats OpenSSH et PEM." + }, "jsonFormatter": { "title": "Formateur JSON", "description": "Formater, valider et modifier des données JSON." @@ -382,6 +386,10 @@ "title": "Calculateur IP / sous-réseau", "description": "IPv4 et IPv6 CIDR : masque, plages et nombre d’adresses." }, + "dnsLookup": { + "title": "Recherche DNS", + "description": "Interrogez les enregistrements DNS d'un domaine — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Générateur de hash", "description": "MD5, SHA-1 et SHA-2 pour texte ou fichiers." diff --git a/apps/web/messages/id.json b/apps/web/messages/id.json index ba7fe9bf..84fe18fb 100644 --- a/apps/web/messages/id.json +++ b/apps/web/messages/id.json @@ -282,6 +282,10 @@ "title": "Dekoder sertifikat / PEM", "description": "Dekode sertifikat X.509 dan CSR: subjek, penerbit, masa berlaku, dan SAN." }, + "sshKeyGenerator": { + "title": "Generator Kunci SSH / RSA", + "description": "Buat pasangan kunci Ed25519 atau RSA di browser. Unduh dalam format OpenSSH dan PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Kalkulator IP / subnet", "description": "IPv4 dan IPv6 CIDR: netmask, rentang, dan jumlah alamat." }, + "dnsLookup": { + "title": "Pencarian DNS", + "description": "Kueri rekaman DNS untuk domain apa pun — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Generator hash", "description": "MD5, SHA-1, dan SHA-2 untuk teks atau file." diff --git a/apps/web/messages/it.json b/apps/web/messages/it.json index db312366..65110e24 100644 --- a/apps/web/messages/it.json +++ b/apps/web/messages/it.json @@ -282,6 +282,10 @@ "title": "Decodificatore certificati / PEM", "description": "Decodifica certificati X.509 e CSR: soggetto, emittente, validità e SAN." }, + "sshKeyGenerator": { + "title": "Generatore di chiavi SSH / RSA", + "description": "Genera coppie di chiavi Ed25519 o RSA. Scarica le chiavi nei formati OpenSSH e PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Calcolatrice IP / subnet", "description": "IPv4 e IPv6 CIDR: maschera, intervalli e conteggio indirizzi." }, + "dnsLookup": { + "title": "Ricerca DNS", + "description": "Interroga i record DNS per qualsiasi dominio — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Generatore di hash", "description": "MD5, SHA-1 e SHA-2 per testo o file." diff --git a/apps/web/messages/ja.json b/apps/web/messages/ja.json index 8deea35f..5ca7b19c 100644 --- a/apps/web/messages/ja.json +++ b/apps/web/messages/ja.json @@ -282,6 +282,10 @@ "title": "証明書 / PEM デコーダ", "description": "X.509 証明書と CSR をデコード: サブジェクト、発行者、有効期間、SAN。" }, + "sshKeyGenerator": { + "title": "SSH / RSA キー生成器", + "description": "ブラウザで Ed25519 または RSA キーペアを生成。OpenSSH および PEM 形式でダウンロード。" + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP/サブネット計算", "description": "IPv4/IPv6 CIDR:ネットマスク、範囲、件数。" }, + "dnsLookup": { + "title": "DNS ルックアップ", + "description": "任意のドメインの DNS レコードを照会 — A、AAAA、MX、TXT、NS、CNAME。" + }, "hashGenerator": { "title": "ハッシュ生成", "description": "テキストやファイルの MD5、SHA-1、SHA-2。" diff --git a/apps/web/messages/ko.json b/apps/web/messages/ko.json index 36daa893..d6aee355 100644 --- a/apps/web/messages/ko.json +++ b/apps/web/messages/ko.json @@ -282,6 +282,10 @@ "title": "인증서 / PEM 디코더", "description": "X.509 인증서와 CSR 디코딩: 주체, 발급자, 유효 기간, SAN." }, + "sshKeyGenerator": { + "title": "SSH / RSA 키 생성기", + "description": "브라우저에서 Ed25519 또는 RSA 키 쌍을 생성합니다. OpenSSH 및 PEM 형식으로 다운로드." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP / 서브넷 계산기", "description": "IPv4·IPv6 CIDR: 넷마스크, 범위, 주소 수." }, + "dnsLookup": { + "title": "DNS 조회", + "description": "도메인의 DNS 레코드를 조회합니다 — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "해시 생성기", "description": "텍스트나 파일용 MD5, SHA-1, SHA-2." diff --git a/apps/web/messages/ms.json b/apps/web/messages/ms.json index 27e5f08d..5ad4958b 100644 --- a/apps/web/messages/ms.json +++ b/apps/web/messages/ms.json @@ -288,6 +288,10 @@ "title": "Pengecam sijil / PEM", "description": "Nyahkod sijil X.509 dan CSR: subjek, penerbit, kesahan, dan SAN." }, + "sshKeyGenerator": { + "title": "Penjana Kunci SSH / RSA", + "description": "Jana pasangan kunci Ed25519 atau RSA dalam pelayar. Muat turun dalam format OpenSSH dan PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -392,6 +396,10 @@ "title": "Kalkulator IP / subnet", "description": "IPv4 dan IPv6 CIDR: topeng rangkaian, julat, dan bilangan alamat." }, + "dnsLookup": { + "title": "Carian DNS", + "description": "Pertanyaan rekod DNS untuk mana-mana domain — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Penjana hash", "description": "MD5, SHA-1 dan SHA-2 untuk teks atau fail." diff --git a/apps/web/messages/nb.json b/apps/web/messages/nb.json index f834c19b..dc479517 100644 --- a/apps/web/messages/nb.json +++ b/apps/web/messages/nb.json @@ -282,6 +282,10 @@ "title": "Sertifikat- / PEM-dekoder", "description": "Dekod X.509-sertifikater og CSR-er: emne, utsteder, gyldighet og SAN." }, + "sshKeyGenerator": { + "title": "SSH / RSA-nøkkelgenerator", + "description": "Generer Ed25519- eller RSA-nøkkelpar i nettleseren. Last ned i OpenSSH- og PEM-format." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP-/subnettkalkulator", "description": "IPv4 og IPv6 CIDR: nettmaske, intervaller og adresser." }, + "dnsLookup": { + "title": "DNS-oppslag", + "description": "Spør etter DNS-poster for et hvilket som helst domene — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Hash-generator", "description": "MD5, SHA-1 og SHA-2 for tekst eller filer." diff --git a/apps/web/messages/nl.json b/apps/web/messages/nl.json index e468812a..312e0d53 100644 --- a/apps/web/messages/nl.json +++ b/apps/web/messages/nl.json @@ -282,6 +282,10 @@ "title": "Certificaat- / PEM-decoder", "description": "Decodeer X.509-certificaten en CSR’s: subject, uitgever, geldigheid en SAN’s." }, + "sshKeyGenerator": { + "title": "SSH / RSA-sleutelgenerator", + "description": "Genereer Ed25519- of RSA-sleutelparen in uw browser. Download in OpenSSH- en PEM-formaat." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP-/subnetcalculator", "description": "IPv4 en IPv6 CIDR: netmasker, bereiken en adressen." }, + "dnsLookup": { + "title": "DNS-opzoeking", + "description": "Zoek DNS-records op voor elk domein — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Hash-generator", "description": "MD5, SHA-1 en SHA-2 voor tekst of bestanden." diff --git a/apps/web/messages/pl.json b/apps/web/messages/pl.json index 80e0ead5..533835c9 100644 --- a/apps/web/messages/pl.json +++ b/apps/web/messages/pl.json @@ -282,6 +282,10 @@ "title": "Dekoder certyfikatu / PEM", "description": "Dekoduj certyfikaty X.509 i CSR: podmiot, wystawca, ważność i SAN." }, + "sshKeyGenerator": { + "title": "Generator kluczy SSH / RSA", + "description": "Generuj pary kluczy Ed25519 lub RSA w przeglądarce. Pobierz w formatach OpenSSH i PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Kalkulator IP / podsieci", "description": "IPv4 i IPv6 CIDR: maska, zakresy i liczba adresów." }, + "dnsLookup": { + "title": "Wyszukiwanie DNS", + "description": "Zapytaj rekordy DNS dla dowolnej domeny — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Generator skrótów", "description": "MD5, SHA-1 i SHA-2 dla tekstu lub plików." diff --git a/apps/web/messages/pt-BR.json b/apps/web/messages/pt-BR.json index 5fcd4fac..e2a114fb 100644 --- a/apps/web/messages/pt-BR.json +++ b/apps/web/messages/pt-BR.json @@ -282,6 +282,10 @@ "title": "Decodificador de certificado / PEM", "description": "Decodifique certificados X.509 e CSRs: assunto, emissor, validade e SAN." }, + "sshKeyGenerator": { + "title": "Gerador de Chaves SSH / RSA", + "description": "Gere pares de chaves Ed25519 ou RSA. Faça o download nos formatos OpenSSH e PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Calculadora IP / sub-rede", "description": "IPv4 e IPv6 CIDR: máscara, intervalos e contagem de endereços." }, + "dnsLookup": { + "title": "Pesquisa DNS", + "description": "Consulte registros DNS de qualquer domínio — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Gerador de hash", "description": "MD5, SHA-1 e SHA-2 para texto ou arquivos." diff --git a/apps/web/messages/pt.json b/apps/web/messages/pt.json index f16e5534..b3ef6f62 100644 --- a/apps/web/messages/pt.json +++ b/apps/web/messages/pt.json @@ -282,6 +282,10 @@ "title": "Descodificador de certificado / PEM", "description": "Descodifique certificados X.509 e CSRs: sujeito, emissor, validade e SAN." }, + "sshKeyGenerator": { + "title": "Gerador de Chaves SSH / RSA", + "description": "Gere pares de chaves Ed25519 ou RSA. Faça o download nos formatos OpenSSH e PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Calculadora IP / sub-rede", "description": "IPv4 e IPv6 CIDR: máscara, intervalos e contagem de endereços." }, + "dnsLookup": { + "title": "Pesquisa DNS", + "description": "Consulte registros DNS de qualquer domínio — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Gerador de hash", "description": "MD5, SHA-1 e SHA-2 para texto ou ficheiros." diff --git a/apps/web/messages/ru.json b/apps/web/messages/ru.json index 9d8379f9..a21ddcd1 100644 --- a/apps/web/messages/ru.json +++ b/apps/web/messages/ru.json @@ -282,6 +282,10 @@ "title": "Декодер сертификата / PEM", "description": "Декодируйте сертификаты X.509 и CSR: субъект, издатель, срок действия, SAN." }, + "sshKeyGenerator": { + "title": "Генератор ключей SSH / RSA", + "description": "Генерируйте пары ключей Ed25519 или RSA в браузере. Скачивайте в форматах OpenSSH и PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Калькулятор IP / подсети", "description": "IPv4 и IPv6 CIDR: маска, диапазоны и число адресов." }, + "dnsLookup": { + "title": "DNS-запрос", + "description": "Запрашивайте DNS-записи для любого домена — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Генератор хешей", "description": "MD5, SHA-1 и SHA-2 для текста или файлов." diff --git a/apps/web/messages/sv.json b/apps/web/messages/sv.json index 5de5dfd0..f9facd7a 100644 --- a/apps/web/messages/sv.json +++ b/apps/web/messages/sv.json @@ -282,6 +282,10 @@ "title": "Certifikat- / PEM-avkodare", "description": "Avkoda X.509-certifikat och CSR:er: ämne, utfärdare, giltighet och SAN." }, + "sshKeyGenerator": { + "title": "SSH / RSA-nyckelgenerator", + "description": "Generera Ed25519- eller RSA-nyckelpar i webbläsaren. Ladda ner i OpenSSH- och PEM-format." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP-/subnetkalkylator", "description": "IPv4 och IPv6 CIDR: nätmask, intervall och adressantal." }, + "dnsLookup": { + "title": "DNS-sökning", + "description": "Fråga DNS-poster för valfri domän — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Hash-generator", "description": "MD5, SHA-1 och SHA-2 för text eller filer." diff --git a/apps/web/messages/tr.json b/apps/web/messages/tr.json index 726c5ebc..6d4198ca 100644 --- a/apps/web/messages/tr.json +++ b/apps/web/messages/tr.json @@ -282,6 +282,10 @@ "title": "Sertifika / PEM çözücü", "description": "X.509 sertifikalarını ve CSR’ları çözümleyin: konu, veren, geçerlilik ve SAN." }, + "sshKeyGenerator": { + "title": "SSH / RSA Anahtar Üreticisi", + "description": "Tarayıcınızda Ed25519 veya RSA anahtar çiftleri oluşturun. OpenSSH ve PEM formatlarında indirin." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP / alt ağ hesaplayıcı", "description": "IPv4 ve IPv6 CIDR: maske, aralıklar ve adres sayısı." }, + "dnsLookup": { + "title": "DNS Sorgulama", + "description": "Herhangi bir alan adı için DNS kayıtlarını sorgulayın — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Hash oluşturucu", "description": "Metin veya dosyalar için MD5, SHA-1 ve SHA-2." diff --git a/apps/web/messages/uk.json b/apps/web/messages/uk.json index bbe5f50c..8b2da03b 100644 --- a/apps/web/messages/uk.json +++ b/apps/web/messages/uk.json @@ -282,6 +282,10 @@ "title": "Декодер сертифіката / PEM", "description": "Декодуйте сертифікати X.509 і CSR: суб’єкт, видавник, строк дії, SAN." }, + "sshKeyGenerator": { + "title": "Генератор ключів SSH / RSA", + "description": "Генеруйте пари ключів Ed25519 або RSA у браузері. Завантажуйте у форматах OpenSSH та PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Калькулятор IP / підмережі", "description": "IPv4 та IPv6 CIDR: маска, діапазони й кількість адрес." }, + "dnsLookup": { + "title": "DNS-запит", + "description": "Запитуйте DNS-записи для будь-якого домену — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Генератор хешів", "description": "MD5, SHA-1 і SHA-2 для тексту або файлів." diff --git a/apps/web/messages/vi.json b/apps/web/messages/vi.json index 73a46666..0c3251d5 100644 --- a/apps/web/messages/vi.json +++ b/apps/web/messages/vi.json @@ -282,6 +282,10 @@ "title": "Bộ giải mã chứng chỉ / PEM", "description": "Giải mã chứng chỉ X.509 và CSR: chủ thể, nhà phát hành, hiệu lực và SAN." }, + "sshKeyGenerator": { + "title": "Trình tạo khóa SSH / RSA", + "description": "Tạo cặp khóa Ed25519 hoặc RSA trong trình duyệt. Tải xuống ở định dạng OpenSSH và PEM." + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "Máy tính IP / mạng con", "description": "IPv4 và IPv6 CIDR: mặt nạ, dải và số địa chỉ." }, + "dnsLookup": { + "title": "Tra cứu DNS", + "description": "Truy vấn bản ghi DNS cho bất kỳ tên miền nào — A, AAAA, MX, TXT, NS, CNAME." + }, "hashGenerator": { "title": "Trình tạo hash", "description": "MD5, SHA-1 và SHA-2 cho văn bản hoặc tệp." diff --git a/apps/web/messages/zh.json b/apps/web/messages/zh.json index 52dbfef7..385f4833 100644 --- a/apps/web/messages/zh.json +++ b/apps/web/messages/zh.json @@ -282,6 +282,10 @@ "title": "证书 / PEM 解码器", "description": "解码 X.509 证书与 CSR:主题、颁发者、有效期与 SAN。" }, + "sshKeyGenerator": { + "title": "SSH / RSA 密钥生成器", + "description": "在浏览器中生成 Ed25519 或 RSA 密钥对,以 OpenSSH 和 PEM 格式下载。" + }, "jsonFormatter": { "title": "JSON Formatter", "description": "Format, validate, and edit JSON data." @@ -382,6 +386,10 @@ "title": "IP / 子网计算器", "description": "IPv4 与 IPv6 CIDR:掩码、范围与地址数量。" }, + "dnsLookup": { + "title": "DNS 查询", + "description": "查询任意域名的 DNS 记录 — A、AAAA、MX、TXT、NS、CNAME、SOA。" + }, "hashGenerator": { "title": "哈希生成器", "description": "文本或文件的 MD5、SHA-1、SHA-2。" diff --git a/apps/web/src/app/app/dns-lookup/page.tsx b/apps/web/src/app/app/dns-lookup/page.tsx new file mode 100644 index 00000000..920e6d32 --- /dev/null +++ b/apps/web/src/app/app/dns-lookup/page.tsx @@ -0,0 +1,12 @@ +import { DnsLookupLayout } from '@/components/dns-lookup/dns-lookup-layout'; +import { generateToolMetadata } from '@/lib/metadata'; + +export const metadata = generateToolMetadata('dns-lookup'); + +export default function DnsLookupPage() { + return ( +
+ +
+ ); +} diff --git a/apps/web/src/components/dns-lookup/dns-lookup-layout.tsx b/apps/web/src/components/dns-lookup/dns-lookup-layout.tsx new file mode 100644 index 00000000..539404b5 --- /dev/null +++ b/apps/web/src/components/dns-lookup/dns-lookup-layout.tsx @@ -0,0 +1,410 @@ +'use client'; + +import { useState, useCallback, useRef } from 'react'; +import { useTranslations } from 'next-intl'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Card, CardContent } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Badge } from '@/components/ui/badge'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { + IconSearch, + IconLoader2, + IconCopy, + IconCheck, + IconAlertCircle, + IconWorld, + IconChevronDown, + IconChevronUp, + IconRefresh, +} from '@tabler/icons-react'; +import { cn } from '@/lib/utils'; + +interface DNSRecord { + type: string; + value: string; + ttl: number | null; + priority: number | null; + extra: Record | null; +} + +interface DNSLookupResult { + domain: string; + records: Record; + errors: Record; +} + +const RECORD_TYPES = ['A', 'AAAA', 'MX', 'TXT', 'NS', 'CNAME', 'SOA', 'CAA', 'PTR'] as const; +type RecordType = (typeof RECORD_TYPES)[number]; + +const RECORD_TYPE_META: Record = { + A: { color: 'text-blue-500', bg: 'bg-blue-500/10 border-blue-500/20', description: 'IPv4 address' }, + AAAA: { color: 'text-violet-500', bg: 'bg-violet-500/10 border-violet-500/20', description: 'IPv6 address' }, + MX: { color: 'text-orange-500', bg: 'bg-orange-500/10 border-orange-500/20', description: 'Mail server' }, + TXT: { color: 'text-green-500', bg: 'bg-green-500/10 border-green-500/20', description: 'Text record' }, + NS: { color: 'text-cyan-500', bg: 'bg-cyan-500/10 border-cyan-500/20', description: 'Nameserver' }, + CNAME: { color: 'text-pink-500', bg: 'bg-pink-500/10 border-pink-500/20', description: 'Canonical name' }, + SOA: { color: 'text-amber-500', bg: 'bg-amber-500/10 border-amber-500/20', description: 'Start of authority' }, + CAA: { color: 'text-red-500', bg: 'bg-red-500/10 border-red-500/20', description: 'Cert authority' }, + PTR: { color: 'text-teal-500', bg: 'bg-teal-500/10 border-teal-500/20', description: 'Reverse DNS' }, +}; + +const DEFAULT_SELECTED: RecordType[] = ['A', 'AAAA', 'MX', 'TXT', 'NS', 'CNAME']; + +function formatTtl(ttl: number): string { + if (ttl < 60) return `${ttl}s`; + if (ttl < 3600) return `${Math.floor(ttl / 60)}m`; + if (ttl < 86400) return `${Math.floor(ttl / 3600)}h`; + return `${Math.floor(ttl / 86400)}d`; +} + +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + return ( + + ); +} + +function RecordTypeBadge({ type }: { type: RecordType }) { + const meta = RECORD_TYPE_META[type]; + return ( + + {type} + + ); +} + +function RecordRow({ record }: { record: DNSRecord }) { + const [expanded, setExpanded] = useState(false); + const hasExtra = record.extra && Object.keys(record.extra).length > 0; + + return ( +
+
+ {record.priority != null && ( + + {record.priority} + + )} + {record.value} +
+ {record.ttl != null && ( + + TTL {formatTtl(record.ttl)} + + )} + {hasExtra && ( + + )} + +
+
+ + {hasExtra && expanded && ( + +
+ {Object.entries(record.extra!).map(([k, v]) => ( +
+ {k}: + {String(v)} +
+ ))} +
+
+ )} +
+
+ ); +} + +function RecordSection({ type, records }: { type: RecordType; records: DNSRecord[] }) { + const meta = RECORD_TYPE_META[type]; + const isEmpty = records.length === 0; + + return ( + + +
+ + {meta.description} +
+ + {records.length} + +
+
+ + {isEmpty ? ( +

No records found

+ ) : ( +
+ {records.map((rec, i) => ( + + ))} +
+ )} +
+
+
+ ); +} + +const QUICK_DOMAINS = ['google.com', 'github.com', 'cloudflare.com', 'example.com']; + +export function DnsLookupLayout() { + const t = useTranslations('DnsLookup'); + const [domain, setDomain] = useState(''); + const [selectedTypes, setSelectedTypes] = useState(DEFAULT_SELECTED); + const [loading, setLoading] = useState(false); + const [result, setResult] = useState(null); + const [globalError, setGlobalError] = useState(null); + const inputRef = useRef(null); + + const toggleType = useCallback((type: RecordType) => { + setSelectedTypes(prev => + prev.includes(type) + ? prev.length > 1 ? prev.filter(t => t !== type) : prev + : [...prev, type] + ); + }, []); + + const lookup = useCallback(async (overrideDomain?: string) => { + const target = (overrideDomain ?? domain).trim().replace(/^https?:\/\//i, '').split('/')[0]; + if (!target) return; + setLoading(true); + setResult(null); + setGlobalError(null); + if (overrideDomain) setDomain(overrideDomain); + + try { + const params = new URLSearchParams({ domain: target }); + selectedTypes.forEach(rt => params.append('record_types', rt)); + const res = await fetch(`/api/backend/dns-lookup/lookup?${params}`, { + credentials: 'include', + headers: { accept: 'application/json' }, + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error(body?.detail || `Request failed (${res.status})`); + } + const data: DNSLookupResult = await res.json(); + if (data.errors?._ ) { + setGlobalError(data.errors._); + } else { + setResult(data); + } + } catch (e) { + setGlobalError(e instanceof Error ? e.message : t('errors.lookupFailed')); + } finally { + setLoading(false); + } + }, [domain, selectedTypes, t]); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') lookup(); + }; + + const hasResults = result && Object.keys(result.records).length > 0; + const resultTypes = result + ? (selectedTypes.filter(rt => rt in result.records) as RecordType[]) + : []; + + return ( + +
+ {/* Search card */} + +
+
+ + setDomain(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={t('placeholder')} + className="pl-9 font-mono text-sm" + disabled={loading} + /> +
+ +
+ + {/* Record type selector */} +
+

{t('recordTypesLabel')}

+
+ {RECORD_TYPES.map(rt => { + const meta = RECORD_TYPE_META[rt]; + const active = selectedTypes.includes(rt); + return ( + + + + + + {meta.description} + + + ); + })} + +
+
+ + {/* Quick domains */} +
+ {t('tryLabel')} + {QUICK_DOMAINS.map(d => ( + + ))} +
+
+ + {/* Global error */} + + {globalError && ( + + + {globalError} + + )} + + + {/* Loading skeleton */} + + {loading && ( + + {selectedTypes.slice(0, 4).map(rt => ( + +
+
+
+
+
+
+ {rt === 'TXT' &&
} +
+ + ))} + + )} + + + {/* Results */} + + {hasResults && !loading && ( + +
+

+ Results for {result!.domain} +

+ +
+ + {resultTypes.map((rt, i) => ( + + + + ))} + + {/* Per-type errors */} + {Object.entries(result!.errors).filter(([k]) => k !== '_').map(([rtype, msg]) => ( +
+ {rtype}: + {msg} +
+ ))} +
+ )} +
+
+ + ); +} diff --git a/apps/web/src/components/sidebar/data/sidebar-data.ts b/apps/web/src/components/sidebar/data/sidebar-data.ts index 01d4f601..63fb38d0 100644 --- a/apps/web/src/components/sidebar/data/sidebar-data.ts +++ b/apps/web/src/components/sidebar/data/sidebar-data.ts @@ -54,6 +54,7 @@ import { IconDeviceGamepad2, IconBucket, IconBrandRedux, + IconWorld, } from '@tabler/icons-react' import { type SidebarData } from '../types' @@ -345,6 +346,12 @@ export const sidebarData: SidebarData = { icon: IconListNumbers, description: 'Search HTTP status codes with descriptions (1xx–5xx).', }, + { + title: 'DNS Lookup', + url: '/app/dns-lookup', + icon: IconWorld, + description: 'Query A, AAAA, MX, TXT, NS, CNAME, SOA, CAA, and PTR records for any domain.', + }, { title: 'IP / Subnet Calculator', url: '/app/ip-subnet-calculator', diff --git a/apps/web/src/lib/metadata.ts b/apps/web/src/lib/metadata.ts index c08a15c7..b3d573c9 100644 --- a/apps/web/src/lib/metadata.ts +++ b/apps/web/src/lib/metadata.ts @@ -129,6 +129,12 @@ export const toolsMetadata: Record = { keywords: ['qr code', 'qr generator', 'qrcode', 'png qr', 'wifi qr', 'vcard qr', 'offline qr'], aiSummary: 'Generate PNG QR codes from any text or URL with custom colors, error correction level, and margin. Download instantly. No server upload — answers "free QR code generator online".', }, + 'dns-lookup': { + title: 'DNS Lookup', + description: 'Query DNS records for any domain: A, AAAA, MX, TXT, NS, CNAME, SOA, CAA, and PTR. See TTL, mail server priority, and SOA details.', + keywords: ['dns lookup', 'dns checker', 'mx record lookup', 'txt record lookup', 'nameserver lookup', 'dns records', 'cname lookup', 'dns query', 'ptr record'], + aiSummary: 'Look up DNS records (A, AAAA, MX, TXT, NS, CNAME, SOA, CAA, PTR) for any domain. Shows TTL values, MX priority, and SOA fields. Answers "check DNS records online", "MX record lookup", or "DNS propagation checker".', + }, 'ip-subnet-calculator': { title: 'IP / Subnet Calculator', description: 'Compute IPv4 and IPv6 CIDR details: netmask, wildcard, broadcast, first and last host, and subnet size. Runs locally in your browser.', diff --git a/apps/web/src/lib/tab-registry.tsx b/apps/web/src/lib/tab-registry.tsx index 1eaa8c47..54470616 100644 --- a/apps/web/src/lib/tab-registry.tsx +++ b/apps/web/src/lib/tab-registry.tsx @@ -43,6 +43,7 @@ const TAB_REGISTRY: Record = { '/app/uuid-generator': dynamic(() => import('@/app/app/uuid-generator/page'), { ssr: false, loading }), '/app/secret-api-key-generator': dynamic(() => import('@/app/app/secret-api-key-generator/page'), { ssr: false, loading }), '/app/qr-code-generator': dynamic(() => import('@/app/app/qr-code-generator/page'), { ssr: false, loading }), + '/app/dns-lookup': dynamic(() => import('@/app/app/dns-lookup/page'), { ssr: false, loading }), '/app/ip-subnet-calculator': dynamic(() => import('@/app/app/ip-subnet-calculator/page'), { ssr: false, loading }), '/app/hash-generator': dynamic(() => import('@/app/app/hash-generator/page'), { ssr: false, loading }), '/app/hmac-generator': dynamic(() => import('@/app/app/hmac-generator/page'), { ssr: false, loading }), diff --git a/apps/web/src/lib/tool-i18n.ts b/apps/web/src/lib/tool-i18n.ts index 9fb7041e..71d05d07 100644 --- a/apps/web/src/lib/tool-i18n.ts +++ b/apps/web/src/lib/tool-i18n.ts @@ -27,6 +27,7 @@ export const TOOL_PATH_TO_MESSAGE_KEY: Record = { '/app/uuid-generator': 'uuidGenerator', '/app/secret-api-key-generator': 'secretApiKeyGenerator', '/app/qr-code-generator': 'qrCodeGenerator', + '/app/dns-lookup': 'dnsLookup', '/app/ip-subnet-calculator': 'ipSubnetCalculator', '/app/hash-generator': 'hashGenerator', '/app/hmac-generator': 'hmacGenerator', From 49300b80f96e319fe05babbca202daa55443fd92 Mon Sep 17 00:00:00 2001 From: itsmeakhil Date: Sat, 9 May 2026 18:21:52 +0530 Subject: [PATCH 2/2] Update tool-i18n.ts --- apps/web/src/lib/tool-i18n.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/src/lib/tool-i18n.ts b/apps/web/src/lib/tool-i18n.ts index 71d05d07..91c5290b 100644 --- a/apps/web/src/lib/tool-i18n.ts +++ b/apps/web/src/lib/tool-i18n.ts @@ -51,7 +51,6 @@ export const TOOL_PATH_TO_MESSAGE_KEY: Record = { '/app/mock-data-generator': 'mockDataGenerator', '/app/unit-converter': 'unitConverter', '/app/svg-optimizer': 'svgOptimizer', - '/app/yaml-formatter': 'yamlFormatter', '/app/ssh-key-generator': 'sshKeyGenerator', '/app/break-room/2048': 'game2048', '/app/break-room/sudoku': 'sudoku',