ui: Add resolver
This commit is contained in:
parent
547e8f1249
commit
ed634abf39
10 changed files with 622 additions and 0 deletions
11
ui/src/lib/api/resolver.ts
Normal file
11
ui/src/lib/api/resolver.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { handleApiResponse } from '$lib/errors';
|
||||||
|
import type { ResolverForm } from '$lib/model/resolver';
|
||||||
|
|
||||||
|
export async function resolve(form: ResolverForm): Promise<any> {
|
||||||
|
const res = await fetch(`/api/resolver`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Accept': 'application/json'},
|
||||||
|
body: JSON.stringify(form),
|
||||||
|
});
|
||||||
|
return await handleApiResponse(res);
|
||||||
|
}
|
139
ui/src/lib/components/resolver/Form.svelte
Normal file
139
ui/src/lib/components/resolver/Form.svelte
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Collapse,
|
||||||
|
Container,
|
||||||
|
FormGroup,
|
||||||
|
Input,
|
||||||
|
Spinner
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import SelectType from '$lib/components/resolver/SelectType.svelte';
|
||||||
|
import SelectResolver from '$lib/components/resolver/SelectResolver.svelte';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
export let value = {
|
||||||
|
domain: "",
|
||||||
|
type: "ANY",
|
||||||
|
resolver: "local",
|
||||||
|
custom: "",
|
||||||
|
};
|
||||||
|
export let showDNSSEC = false;
|
||||||
|
|
||||||
|
export let sortedDomains = [];
|
||||||
|
export let request_pending = false;
|
||||||
|
|
||||||
|
function submitRequest() {
|
||||||
|
request_pending = true;
|
||||||
|
dispatch('submit', {value, showDNSSEC});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form class="pt-3 pb-5" on:submit|preventDefault={submitRequest}>
|
||||||
|
<FormGroup>
|
||||||
|
<label for="domain">
|
||||||
|
{$t('common.domain')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
aria-describedby="domainHelpBlock"
|
||||||
|
id="domain"
|
||||||
|
list="my-domains"
|
||||||
|
required
|
||||||
|
placeholder="happydomain.org"
|
||||||
|
bind:value={value.domain}
|
||||||
|
/>
|
||||||
|
<div id="domainHelpBlock" class="form-text">
|
||||||
|
{$t('resolver.domain-description')}
|
||||||
|
</div>
|
||||||
|
<datalist id="my-domains">
|
||||||
|
{#each sortedDomains as dn (dn.id)}
|
||||||
|
<option>
|
||||||
|
{dn.domain}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</datalist>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<Button type="button" color="secondary" id="settingsToggler">
|
||||||
|
{$t('resolver.advanced')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Collapse toggler="#settingsToggler">
|
||||||
|
<FormGroup>
|
||||||
|
<label for="select-type">
|
||||||
|
{$t('common.field')}
|
||||||
|
</label>
|
||||||
|
<SelectType
|
||||||
|
aria-describedby="typeHelpBlock"
|
||||||
|
id="select-type"
|
||||||
|
required
|
||||||
|
bind:value={value.type}
|
||||||
|
/>
|
||||||
|
<div id="typeHelpBlock" class="form-text">
|
||||||
|
{$t('resolver.field-description')}
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<label for="select-resolver">
|
||||||
|
{$t('common.resolver')}
|
||||||
|
</label>
|
||||||
|
<SelectResolver
|
||||||
|
aria-describedby="resolverHelpBlock"
|
||||||
|
id="select-resolver"
|
||||||
|
required
|
||||||
|
bind:value={value.resolver}
|
||||||
|
/>
|
||||||
|
<div id="resolverHelpBlock" class="form-text">
|
||||||
|
{$t('resolver.resolver-description')}
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{#if value.resolver === "custom"}
|
||||||
|
<FormGroup>
|
||||||
|
<label for="custom-resolver">
|
||||||
|
{$t('resolver.custom')}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
aria-describedby="customResolverHelpBlock"
|
||||||
|
id="custom-resolver"
|
||||||
|
required={value.resolver === 'custom'}
|
||||||
|
placeholder="127.0.0.1"
|
||||||
|
bind:value={value.custom}
|
||||||
|
/>
|
||||||
|
<div id="customResolverHelpBlock" class="form-text">
|
||||||
|
{$t('resolver.custom-description')}
|
||||||
|
</div>
|
||||||
|
</FormGroup>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="checkbox"
|
||||||
|
label={$t('resolver.showDNSSEC')}
|
||||||
|
id="showDNSSEC"
|
||||||
|
bind:value={showDNSSEC}
|
||||||
|
name="showDNSSEC"
|
||||||
|
class="mb-3"
|
||||||
|
/>
|
||||||
|
</Collapse>
|
||||||
|
|
||||||
|
<div class="ml-3 mr-3">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
class="float-end"
|
||||||
|
color="primary"
|
||||||
|
disabled={request_pending}
|
||||||
|
>
|
||||||
|
{#if request_pending}
|
||||||
|
<Spinner label={$t('common.spinning')} size="sm" />
|
||||||
|
{/if}
|
||||||
|
{$t('common.run')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
30
ui/src/lib/components/resolver/SelectResolver.svelte
Normal file
30
ui/src/lib/components/resolver/SelectResolver.svelte
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
Input,
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import { resolvers } from '$lib/resolver';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
|
||||||
|
export let value = "local";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="select"
|
||||||
|
bind:value={value}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
{#each Object.keys(resolvers) as resolver_kind}
|
||||||
|
<optgroup label={resolver_kind}>
|
||||||
|
{#each resolvers[resolver_kind] as resolver}
|
||||||
|
<option value={resolver.value}>
|
||||||
|
{resolver.text}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</optgroup>
|
||||||
|
{/each}
|
||||||
|
<option value="custom">
|
||||||
|
{$t('resolver.custom')}
|
||||||
|
</option>
|
||||||
|
</Input>
|
19
ui/src/lib/components/resolver/SelectType.svelte
Normal file
19
ui/src/lib/components/resolver/SelectType.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Input,
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import { dns_common_types } from '$lib/dns';
|
||||||
|
|
||||||
|
export let value = "ANY";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="select"
|
||||||
|
bind:value={value}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
{#each dns_common_types as option}
|
||||||
|
<option>{option}</option>
|
||||||
|
{/each}
|
||||||
|
</Input>
|
130
ui/src/lib/dns.ts
Normal file
130
ui/src/lib/dns.ts
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
export const dns_common_types: Array<string> = ['ANY', 'A', 'AAAA', 'NS', 'SRV', 'MX', 'TXT', 'SOA'];
|
||||||
|
|
||||||
|
export function nsclass(input: number): string {
|
||||||
|
switch (input) {
|
||||||
|
case 1:
|
||||||
|
return 'IN'
|
||||||
|
case 3:
|
||||||
|
return 'CH'
|
||||||
|
case 4:
|
||||||
|
return 'HS'
|
||||||
|
case 254:
|
||||||
|
return 'NONE'
|
||||||
|
default:
|
||||||
|
return '##'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nsttl(input: number): string {
|
||||||
|
let ret = '';
|
||||||
|
|
||||||
|
if (input / 86400 >= 1) {
|
||||||
|
ret = Math.floor(input / 86400) + 'd '
|
||||||
|
input = input % 86400
|
||||||
|
}
|
||||||
|
if (input / 3600 >= 1) {
|
||||||
|
ret = Math.floor(input / 3600) + 'h '
|
||||||
|
input = input % 3600
|
||||||
|
}
|
||||||
|
if (input / 60 >= 1) {
|
||||||
|
ret = Math.floor(input / 60) + 'm '
|
||||||
|
input = input % 60
|
||||||
|
}
|
||||||
|
if (input >= 1) {
|
||||||
|
ret = Math.floor(input) + 's'
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
export function nsrrtype(input: number|string): string {
|
||||||
|
switch (input) {
|
||||||
|
case '1': case 1: return 'A'
|
||||||
|
case '2': case 2: return 'NS'
|
||||||
|
case '3': case 3: return 'MD'
|
||||||
|
case '4': case 4: return 'MF'
|
||||||
|
case '5': case 5: return 'CNAME'
|
||||||
|
case '6': case 6: return 'SOA'
|
||||||
|
case '7': case 7: return 'MB'
|
||||||
|
case '8': case 8: return 'MG'
|
||||||
|
case '9': case 9: return 'MR'
|
||||||
|
case '10': case 10: return 'NULL'
|
||||||
|
case '11': case 11: return 'WKS'
|
||||||
|
case '12': case 12: return 'PTR'
|
||||||
|
case '13': case 13: return 'HINFO'
|
||||||
|
case '14': case 14: return 'MINFO'
|
||||||
|
case '15': case 15: return 'MX'
|
||||||
|
case '16': case 16: return 'TXT'
|
||||||
|
case '17': case 17: return 'RP'
|
||||||
|
case '18': case 18: return 'AFSDB'
|
||||||
|
case '19': case 19: return 'X25'
|
||||||
|
case '20': case 20: return 'ISDN'
|
||||||
|
case '21': case 21: return 'RT'
|
||||||
|
case '22': case 22: return 'NSAP'
|
||||||
|
case '23': case 23: return 'NSAP-PTR'
|
||||||
|
case '24': case 24: return 'SIG'
|
||||||
|
case '25': case 25: return 'KEY'
|
||||||
|
case '26': case 26: return 'PX'
|
||||||
|
case '27': case 27: return 'GPOS'
|
||||||
|
case '28': case 28: return 'AAAA'
|
||||||
|
case '29': case 29: return 'LOC'
|
||||||
|
case '30': case 30: return 'NXT'
|
||||||
|
case '31': case 31: return 'EID'
|
||||||
|
case '32': case 32: return 'NIMLOC'
|
||||||
|
case '33': case 33: return 'SRV'
|
||||||
|
case '34': case 34: return 'ATMA'
|
||||||
|
case '35': case 35: return 'NAPTR'
|
||||||
|
case '36': case 36: return 'KX'
|
||||||
|
case '37': case 37: return 'CERT'
|
||||||
|
case '38': case 38: return 'A6'
|
||||||
|
case '39': case 39: return 'DNAME'
|
||||||
|
case '40': case 40: return 'SINK'
|
||||||
|
case '41': case 41: return 'OPT'
|
||||||
|
case '42': case 42: return 'APL'
|
||||||
|
case '43': case 43: return 'DS'
|
||||||
|
case '44': case 44: return 'SSHFP'
|
||||||
|
case '45': case 45: return 'IPSECKEY'
|
||||||
|
case '46': case 46: return 'RRSIG'
|
||||||
|
case '47': case 47: return 'NSEC'
|
||||||
|
case '48': case 48: return 'DNSKEY'
|
||||||
|
case '49': case 49: return 'DHCID'
|
||||||
|
case '50': case 50: return 'NSEC3'
|
||||||
|
case '51': case 51: return 'NSEC3PARAM'
|
||||||
|
case '52': case 52: return 'TLSA'
|
||||||
|
case '53': case 53: return 'SMIMEA'
|
||||||
|
case '55': case 55: return 'HIP'
|
||||||
|
case '56': case 56: return 'NINFO'
|
||||||
|
case '57': case 57: return 'RKEY'
|
||||||
|
case '58': case 58: return 'TALINK'
|
||||||
|
case '59': case 59: return 'CDS'
|
||||||
|
case '60': case 60: return 'CDNSKEY'
|
||||||
|
case '61': case 61: return 'OPENPGPKEY'
|
||||||
|
case '62': case 62: return 'CSYNC'
|
||||||
|
case '63': case 63: return 'ZONEMD'
|
||||||
|
case '99': case 99: return 'SPF'
|
||||||
|
case '100': case 100: return 'UINFO'
|
||||||
|
case '101': case 101: return 'UID'
|
||||||
|
case '102': case 102: return 'GID'
|
||||||
|
case '103': case 103: return 'UNSPEC'
|
||||||
|
case '104': case 104: return 'NID'
|
||||||
|
case '105': case 105: return 'L32'
|
||||||
|
case '106': case 106: return 'L64'
|
||||||
|
case '107': case 107: return 'LP'
|
||||||
|
case '108': case 108: return 'EUI48'
|
||||||
|
case '109': case 109: return 'EUI64'
|
||||||
|
case '249': case 249: return 'TKEY'
|
||||||
|
case '250': case 250: return 'TSIG'
|
||||||
|
case '251': case 251: return 'IXFR'
|
||||||
|
case '252': case 252: return 'AXFR'
|
||||||
|
case '253': case 253: return 'MAILB'
|
||||||
|
case '254': case 254: return 'MAILA'
|
||||||
|
case '256': case 256: return 'URI'
|
||||||
|
case '257': case 257: return 'CAA'
|
||||||
|
case '258': case 258: return 'AVC'
|
||||||
|
case '259': case 259: return 'DOA'
|
||||||
|
case '260': case 260: return 'AMTRELAY'
|
||||||
|
case '32768': case 32768: return 'TA'
|
||||||
|
case '32769': case 32769: return 'DLV'
|
||||||
|
default: return '#'
|
||||||
|
}
|
||||||
|
}
|
6
ui/src/lib/model/resolver.ts
Normal file
6
ui/src/lib/model/resolver.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export interface ResolverForm {
|
||||||
|
domain: string;
|
||||||
|
type: string;
|
||||||
|
resolver: string;
|
||||||
|
custom?: string;
|
||||||
|
};
|
96
ui/src/lib/resolver.ts
Normal file
96
ui/src/lib/resolver.ts
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
export const resolvers = {
|
||||||
|
Unfiltered: [
|
||||||
|
{ value: 'local', text: 'Local resolver' },
|
||||||
|
{ value: '1.1.1.1', text: 'Cloudflare DNS resolver' },
|
||||||
|
{ value: '4.2.2.1', text: 'Level3 resolver' },
|
||||||
|
{ value: '8.8.8.8', text: 'Google Public DNS resolver' },
|
||||||
|
{ value: '9.9.9.10', text: 'Quad9 DNS resolver without security blocklist' },
|
||||||
|
{ value: '64.6.64.6', text: 'Verisign DNS resolver' },
|
||||||
|
{ value: '74.82.42.42', text: 'Hurricane Electric DNS resolver' },
|
||||||
|
{ value: '208.67.222.222', text: 'OpenDNS resolver' },
|
||||||
|
{ value: '8.26.56.26', text: 'Comodo Secure DNS resolver' },
|
||||||
|
{ value: '199.85.126.10', text: 'Norton ConnectSafe DNS resolver' },
|
||||||
|
{ value: '198.54.117.10', text: 'SafeServe DNS resolver' },
|
||||||
|
{ value: '84.200.69.80', text: 'DNS.WATCH resolver' },
|
||||||
|
{ value: '185.121.177.177', text: 'OpenNIC DNS resolver' },
|
||||||
|
{ value: '37.235.1.174', text: 'FreeDNS resolver' },
|
||||||
|
{ value: '80.80.80.80', text: 'Freenom World DNS resolver' },
|
||||||
|
{ value: '216.131.65.63', text: 'StrongDNS resolver' },
|
||||||
|
{ value: '94.140.14.140', text: 'AdGuard non-filtering DNS resolver' },
|
||||||
|
{ value: '91.239.100.100', text: 'Uncensored DNS resolver' },
|
||||||
|
{ value: '216.146.35.35', text: 'Dyn DNS resolver' },
|
||||||
|
{ value: '77.88.8.8', text: 'Yandex.DNS resolver' },
|
||||||
|
{ value: '129.250.35.250', text: 'NTT DNS resolver' },
|
||||||
|
{ value: '223.5.5.5', text: 'AliDNS resolver' },
|
||||||
|
{ value: '1.2.4.8', text: 'CNNIC SDNS resolver' },
|
||||||
|
{ value: '119.29.29.29', text: 'DNSPod resolver' },
|
||||||
|
{ value: '114.215.126.16', text: 'oneDNS resolver' },
|
||||||
|
{ value: '124.251.124.251', text: 'cloudxns resolver' },
|
||||||
|
{ value: '114.114.114.114', text: 'Baidu DNS resolver' },
|
||||||
|
{ value: '156.154.70.1', text: 'DNS Advantage resolver' },
|
||||||
|
{ value: '87.118.111.215', text: 'FoolDNS resolver' },
|
||||||
|
{ value: '101.101.101.101', text: 'Quad 101 DNS resolver' },
|
||||||
|
{ value: '114.114.114.114', text: '114DNS resolver' },
|
||||||
|
{ value: '168.95.1.1', text: 'HiNet DNS resolver' },
|
||||||
|
{ value: '80.67.169.12', text: 'French Data Network DNS resolver' },
|
||||||
|
{ value: '81.218.119.11', text: 'GreenTeamDNS resolver' },
|
||||||
|
{ value: '208.76.50.50', text: 'SmartViper DNS resolver' },
|
||||||
|
{ value: '23.253.163.53', text: 'Alternate DNS resolver' },
|
||||||
|
{ value: '109.69.8.51', text: 'puntCAT DNS resolver' },
|
||||||
|
{ value: '156.154.70.1', text: 'Neustar DNS resolver' },
|
||||||
|
{ value: '101.226.4.6', text: 'DNSpai resolver' }
|
||||||
|
// Your open resolver here? Don't hesitate to contribute to the project!
|
||||||
|
],
|
||||||
|
Filtered: [
|
||||||
|
{ value: '1.1.1.2', text: 'Cloudflare Malware Blocking Only DNS resolver' },
|
||||||
|
{ value: '1.1.1.3', text: 'Cloudflare Malware and Adult Content Blocking Only DNS resolver' },
|
||||||
|
{ value: '9.9.9.9', text: 'Quad9 DNS resolver' },
|
||||||
|
{ value: '94.140.14.14', text: 'AdGuard default DNS resolver' },
|
||||||
|
{ value: '94.140.14.15', text: 'AdGuard family protection DNS resolver' },
|
||||||
|
{ value: '77.88.8.2', text: 'Yandex.DNS Safe resolver' },
|
||||||
|
{ value: '77.88.8.3', text: 'Yandex.DNS Family resolver' },
|
||||||
|
{ value: '156.154.70.2', text: 'DNS Advantage Threat Protection resolver' },
|
||||||
|
{ value: '156.154.70.3', text: 'DNS Advantage Family Secure resolver' },
|
||||||
|
{ value: '156.154.70.4', text: 'DNS Advantage Business Secure resolver' },
|
||||||
|
{ value: '185.228.168.168', text: 'CleanBrowsing Family Filter DNS resolver' },
|
||||||
|
{ value: '185.228.168.10', text: 'CleanBrowsing Adult Filter DNS resolver' }
|
||||||
|
// Your open resolver here? Don't hesitate to contribute to the project!
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function recordsFields (rrtype: number): Array<string> {
|
||||||
|
switch (rrtype) {
|
||||||
|
case 1:
|
||||||
|
return ['A']
|
||||||
|
case 2:
|
||||||
|
return ['Ns']
|
||||||
|
case 5:
|
||||||
|
return ['Target']
|
||||||
|
case 6:
|
||||||
|
return ['Ns', 'Mbox', 'Serial', 'Refresh', 'Retry', 'Expire', 'Minttl']
|
||||||
|
case 12:
|
||||||
|
return ['Ptr']
|
||||||
|
case 13:
|
||||||
|
return ['Cpu', 'Os']
|
||||||
|
case 15:
|
||||||
|
return ['Mx', 'Preference']
|
||||||
|
case 16:
|
||||||
|
case 99:
|
||||||
|
return ['Txt']
|
||||||
|
case 28:
|
||||||
|
return ['AAAA']
|
||||||
|
case 33:
|
||||||
|
return ['Target', 'Port', 'Priority', 'Weight']
|
||||||
|
case 43:
|
||||||
|
return ['KeyTag', 'Algorithm', 'DigestType', 'Digest']
|
||||||
|
case 44:
|
||||||
|
return ['Algorithm', 'Type', 'FingerPrint']
|
||||||
|
case 46:
|
||||||
|
return ['TypeCovered', 'Algorithm', 'Labels', 'OrigTtl', 'Expiration', 'Inception', 'KeyTag', 'SignerName', 'Signature']
|
||||||
|
case 52:
|
||||||
|
return ['Usage', 'Selector', 'MatchingType', 'Certificate']
|
||||||
|
default:
|
||||||
|
console.warn('Unknown RRtype asked fields: ', rrtype)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
40
ui/src/routes/resolver/+page.svelte
Normal file
40
ui/src/routes/resolver/+page.svelte
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Col,
|
||||||
|
Row,
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import ResolverForm from '$lib/components/resolver/Form.svelte';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
import { toasts } from '$lib/stores/toasts';
|
||||||
|
|
||||||
|
export let data = { };
|
||||||
|
let request_pending = false;
|
||||||
|
|
||||||
|
function resolveDomain(event) {
|
||||||
|
const form = event.detail.value;
|
||||||
|
const showDNSSEC = event.detail.showDNSSEC;
|
||||||
|
|
||||||
|
request_pending = true;
|
||||||
|
goto('/resolver/' + encodeURIComponent(form.domain), {
|
||||||
|
state: {form, showDNSSEC},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Container fluid class="d-flex flex-column">
|
||||||
|
<Row class="flex-grow-1">
|
||||||
|
<Col md={{offset: 2, size: 8}} class="pt-4 pb-5">
|
||||||
|
<h1 class="text-center mb-3">
|
||||||
|
{$t('menu.dns-resolver')}
|
||||||
|
</h1>
|
||||||
|
<ResolverForm
|
||||||
|
bind:request_pending={request_pending}
|
||||||
|
on:submit={resolveDomain}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
143
ui/src/routes/resolver/[domain]/+page.svelte
Normal file
143
ui/src/routes/resolver/[domain]/+page.svelte
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
Col,
|
||||||
|
Row,
|
||||||
|
} from 'sveltestrap';
|
||||||
|
|
||||||
|
import { resolve } from '$lib/api/resolver';
|
||||||
|
import ResolverForm from '$lib/components/resolver/Form.svelte';
|
||||||
|
import { nsttl, nsrrtype } from '$lib/dns';
|
||||||
|
import { recordsFields } from '$lib/resolver';
|
||||||
|
import { t } from '$lib/translations';
|
||||||
|
import { toasts } from '$lib/stores/toasts';
|
||||||
|
|
||||||
|
export let data = { };
|
||||||
|
let question = null;
|
||||||
|
let responses = null;
|
||||||
|
let request_pending = false;
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (!data.form) {
|
||||||
|
data.form = { };
|
||||||
|
}
|
||||||
|
data.form.domain = data.domain;
|
||||||
|
|
||||||
|
resolve(data.form)
|
||||||
|
.then(
|
||||||
|
(response) => {
|
||||||
|
question = Object.assign({ }, data.form)
|
||||||
|
if (response.Answer) {
|
||||||
|
responses = response.Answer
|
||||||
|
} else {
|
||||||
|
responses = 'no-answer'
|
||||||
|
}
|
||||||
|
request_pending = false
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
toasts.addErrorToast({
|
||||||
|
title: $t('errors.resolve'),
|
||||||
|
message: error,
|
||||||
|
timeout: 5000,
|
||||||
|
})
|
||||||
|
request_pending = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function filteredResponses(responses, showDNSSEC) {
|
||||||
|
if (!responses) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showDNSSEC) {
|
||||||
|
return responses
|
||||||
|
} else {
|
||||||
|
return responses.filter(rr => (rr.Hdr.Rrtype !== 46 && rr.Hdr.Rrtype !== 47 && rr.Hdr.Rrtype !== 50))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function responseByType(filteredResponses) {
|
||||||
|
const ret = { };
|
||||||
|
|
||||||
|
for (const i in filteredResponses) {
|
||||||
|
if (!ret[filteredResponses[i].Hdr.Rrtype]) {
|
||||||
|
ret[filteredResponses[i].Hdr.Rrtype] = []
|
||||||
|
}
|
||||||
|
ret[filteredResponses[i].Hdr.Rrtype].push(filteredResponses[i])
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDomain(event) {
|
||||||
|
const form = event.detail.value;
|
||||||
|
const showDNSSEC = event.detail.showDNSSEC;
|
||||||
|
|
||||||
|
request_pending = true;
|
||||||
|
goto('/resolver/' + encodeURIComponent(form.domain), {
|
||||||
|
state: {form, showDNSSEC},
|
||||||
|
noScroll: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Container fluid class="flex-fill d-flex flex-column">
|
||||||
|
<Row class="flex-grow-1">
|
||||||
|
<Col md={{offset: 0, size: 4}} class="bg-light pt-3 pb-5">
|
||||||
|
<div class="pt-2 sticky-top">
|
||||||
|
<h1 class="text-center mb-3">
|
||||||
|
{$t('menu.dns-resolver')}
|
||||||
|
</h1>
|
||||||
|
<ResolverForm
|
||||||
|
bind:request_pending={request_pending}
|
||||||
|
value={data.form}
|
||||||
|
on:submit={resolveDomain}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
{#if responses === 'no-answer'}
|
||||||
|
<Col md="8" class="pt-2">
|
||||||
|
<h3>{$t('common.records', { number: 0, type: question.type })}</h3>
|
||||||
|
</Col>
|
||||||
|
{:else if responses}
|
||||||
|
<Col md="8" class="pt-2">
|
||||||
|
{@const resByType = responseByType(filteredResponses(responses, data.showDNSSEC))}
|
||||||
|
{#each Object.keys(resByType) as type}
|
||||||
|
{@const rrs = resByType[type]}
|
||||||
|
<div>
|
||||||
|
<h3>{$t('common.records', { number: rrs.length, type: nsrrtype(type) })}</h3>
|
||||||
|
<table class="table table-hover table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{#each recordsFields(Number(type)) as field}
|
||||||
|
<th>
|
||||||
|
{$t('record.' + field)}
|
||||||
|
</th>
|
||||||
|
{/each}
|
||||||
|
<th>
|
||||||
|
{$t('resolver.ttl')}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each rrs as record}
|
||||||
|
<tr>
|
||||||
|
{#each recordsFields(Number(type)) as field}
|
||||||
|
<td>
|
||||||
|
{record[field]}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
<td>
|
||||||
|
{nsttl(Number(record.Hdr.Ttl))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</Col>
|
||||||
|
{/if}
|
||||||
|
</Row>
|
||||||
|
</Container>
|
8
ui/src/routes/resolver/[domain]/+page.ts
Normal file
8
ui/src/routes/resolver/[domain]/+page.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import type { Load } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const load: Load = async({ params }) => {
|
||||||
|
return {
|
||||||
|
...history.state,
|
||||||
|
domain: params.domain,
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue