Include intermediate subdomains without service in list
Closes: https://github.com/happyDomain/happydomain/issues/26
This commit is contained in:
parent
e491485773
commit
3211f8e6f9
|
@ -82,7 +82,43 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isCNAME(services) || isPTR(services)}
|
{#if services.length === 0}
|
||||||
|
<div id={dn}>
|
||||||
|
{#if !reverseZone}
|
||||||
|
<h2
|
||||||
|
class="sticky-top"
|
||||||
|
style="background: white; z-index: 1"
|
||||||
|
>
|
||||||
|
<span style="white-space: nowrap">
|
||||||
|
<Icon
|
||||||
|
name="plus-square-dotted"
|
||||||
|
title="Intermediate domain with no service"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="font-monospace"
|
||||||
|
title={fqdn(dn, origin.domain)}
|
||||||
|
>
|
||||||
|
{#if reverseZone}
|
||||||
|
{unreverseDomain(fqdn(dn, origin.domain))}
|
||||||
|
{:else}
|
||||||
|
{fqdn(dn, origin.domain)}
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
class="ml-2"
|
||||||
|
on:click={() => dispatch("new-service")}
|
||||||
|
>
|
||||||
|
<Icon name="plus" />
|
||||||
|
{$t('service.add')}
|
||||||
|
</Button>
|
||||||
|
</h2>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{:else if isCNAME(services) || isPTR(services)}
|
||||||
<div id={dn}>
|
<div id={dn}>
|
||||||
<h2
|
<h2
|
||||||
class="sticky-top"
|
class="sticky-top"
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
export let origin: DomainInList | Domain;
|
export let origin: DomainInList | Domain;
|
||||||
export let sortedDomains: Array<string>;
|
export let sortedDomains: Array<string>;
|
||||||
|
export let sortedDomainsWithIntermediate: Array<string>;
|
||||||
export let zone: Zone;
|
export let zone: Zone;
|
||||||
|
|
||||||
let aliases: Record<string, Array<string>>;
|
let aliases: Record<string, Array<string>>;
|
||||||
|
@ -69,7 +70,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each sortedDomains as dn}
|
{#each sortedDomainsWithIntermediate as dn}
|
||||||
<SubdomainItem
|
<SubdomainItem
|
||||||
aliases={aliases[dn]?aliases[dn]:[]}
|
aliases={aliases[dn]?aliases[dn]:[]}
|
||||||
{dn}
|
{dn}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!--
|
||||||
|
This file is part of the happyDomain (R) project.
|
||||||
|
Copyright (c) 2022-2024 happyDomain
|
||||||
|
Authors: Pierre-Olivier Mercier, et al.
|
||||||
|
|
||||||
|
This program is offered under a commercial and under the AGPL license.
|
||||||
|
For commercial licensing, contact us at <contact@happydomain.org>.
|
||||||
|
|
||||||
|
For AGPL licensing:
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type { DomainInList } from '$lib/model/domain';
|
||||||
|
import { fqdn } from '$lib/dns';
|
||||||
|
|
||||||
|
export let domains: Array<string>;
|
||||||
|
export let origin: DomainInList;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each domains as dn}
|
||||||
|
<a
|
||||||
|
href={'#' + (dn?dn:'@')}
|
||||||
|
title={fqdn(dn, origin.domain)}
|
||||||
|
class="d-block text-truncate font-monospace text-muted text-decoration-none"
|
||||||
|
style={'max-width: none; padding-left: ' + (dn === '' ? 0 : (dn.split('.').length * 10)) + 'px'}
|
||||||
|
>
|
||||||
|
{fqdn(dn, origin.domain)}
|
||||||
|
</a>
|
||||||
|
{/each}
|
|
@ -31,6 +31,7 @@ import { refreshDomains } from '$lib/stores/domains';
|
||||||
|
|
||||||
export const thisZone: Writable<null | Zone> = writable(null);
|
export const thisZone: Writable<null | Zone> = writable(null);
|
||||||
|
|
||||||
|
// sortedDomains returns all subdomains, sorted
|
||||||
export const sortedDomains = derived(
|
export const sortedDomains = derived(
|
||||||
thisZone,
|
thisZone,
|
||||||
($thisZone: null|Zone) => {
|
($thisZone: null|Zone) => {
|
||||||
|
@ -46,6 +47,36 @@ export const sortedDomains = derived(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// sortedDomainsWithIntermediate returns all subdomains, sorted, with intermediate subdomains
|
||||||
|
export const sortedDomainsWithIntermediate = derived(
|
||||||
|
sortedDomains,
|
||||||
|
($sortedDomains: null|Array<string>) => {
|
||||||
|
if (!$sortedDomains || $sortedDomains.length <= 1) {
|
||||||
|
return $sortedDomains;
|
||||||
|
}
|
||||||
|
const domains: Array<string> = [$sortedDomains[0]];
|
||||||
|
|
||||||
|
let previous = domains[0].split('.');
|
||||||
|
for (let i = 1; i < $sortedDomains.length; i++) {
|
||||||
|
const cur = $sortedDomains[i].split('.');
|
||||||
|
|
||||||
|
if (previous.length < cur.length && previous[0] !== cur[cur.length - previous.length]) {
|
||||||
|
domains.push(cur.slice(cur.length - previous.length).join('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (previous.length + 1 < cur.length) {
|
||||||
|
previous = cur.slice(cur.length - previous.length - 1);
|
||||||
|
domains.push(previous.join('.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
domains.push(cur.join('.'));
|
||||||
|
previous = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return domains;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export async function getZone(domain: DomainInList | Domain, zoneId: string) {
|
export async function getZone(domain: DomainInList | Domain, zoneId: string) {
|
||||||
thisZone.set(null);
|
thisZone.set(null);
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,12 @@
|
||||||
import ModalViewZone, { controls as ctrlViewZone } from '$lib/components/ModalViewZone.svelte';
|
import ModalViewZone, { controls as ctrlViewZone } from '$lib/components/ModalViewZone.svelte';
|
||||||
import NewSubdomainPath, { controls as ctrlNewSubdomain } from '$lib/components/NewSubdomainPath.svelte';
|
import NewSubdomainPath, { controls as ctrlNewSubdomain } from '$lib/components/NewSubdomainPath.svelte';
|
||||||
import NewSubdomainModal from '$lib/components/domains/NewSubdomainModal.svelte';
|
import NewSubdomainModal from '$lib/components/domains/NewSubdomainModal.svelte';
|
||||||
import { fqdn } from '$lib/dns';
|
import SubdomainListTiny from '$lib/components/domains/SubdomainListTiny.svelte';
|
||||||
|
import { fqdn, isReverseZone } from '$lib/dns';
|
||||||
import type { Domain, DomainInList } from '$lib/model/domain';
|
import type { Domain, DomainInList } from '$lib/model/domain';
|
||||||
import type { ZoneMeta } from '$lib/model/zone';
|
import type { ZoneMeta } from '$lib/model/zone';
|
||||||
import { domains, domains_by_groups, domains_idx, refreshDomains } from '$lib/stores/domains';
|
import { domains, domains_by_groups, domains_idx, refreshDomains } from '$lib/stores/domains';
|
||||||
import { retrieveZone as StoreRetrieveZone, sortedDomains, thisZone } from '$lib/stores/thiszone';
|
import { retrieveZone as StoreRetrieveZone, sortedDomains, sortedDomainsWithIntermediate, thisZone } from '$lib/stores/thiszone';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
|
||||||
export let data: {domain: DomainInList;};
|
export let data: {domain: DomainInList;};
|
||||||
|
@ -175,7 +176,7 @@
|
||||||
<Icon name="chevron-left" />
|
<Icon name="chevron-left" />
|
||||||
Retour à la zone
|
Retour à la zone
|
||||||
</Button>
|
</Button>
|
||||||
{:else if $page.data.streamed && $sortedDomains}
|
{:else if $page.data.streamed && $sortedDomainsWithIntermediate}
|
||||||
<div class="d-flex gap-2 pb-2 sticky-top bg-light" style="padding-top: 10px">
|
<div class="d-flex gap-2 pb-2 sticky-top bg-light" style="padding-top: 10px">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -250,16 +251,17 @@
|
||||||
</div>
|
</div>
|
||||||
{#await $page.data.streamed.zone then z}
|
{#await $page.data.streamed.zone then z}
|
||||||
<div style="min-height:0; overflow-y: auto;">
|
<div style="min-height:0; overflow-y: auto;">
|
||||||
{#each $sortedDomains as dn}
|
{#if isReverseZone(data.domain.domain)}
|
||||||
<a
|
<SubdomainListTiny
|
||||||
href={'#' + (dn?dn:'@')}
|
domains={$sortedDomains}
|
||||||
title={fqdn(dn, data.domain.domain)}
|
origin={data.domain}
|
||||||
class="d-block text-truncate font-monospace text-muted text-decoration-none"
|
/>
|
||||||
style={'max-width: none; padding-left: ' + (dn === '' ? 0 : (dn.split('.').length * 10)) + 'px'}
|
{:else}
|
||||||
>
|
<SubdomainListTiny
|
||||||
{fqdn(dn, data.domain.domain)}
|
domains={$sortedDomainsWithIntermediate}
|
||||||
</a>
|
origin={data.domain}
|
||||||
{/each}
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -267,7 +269,7 @@
|
||||||
<div class="flex-fill" />
|
<div class="flex-fill" />
|
||||||
|
|
||||||
{#if $page.data.isZonePage && data.domain.zone_history && $domains_idx[selectedDomain] && data.domain.id === $domains_idx[selectedDomain].id}
|
{#if $page.data.isZonePage && data.domain.zone_history && $domains_idx[selectedDomain] && data.domain.id === $domains_idx[selectedDomain].id}
|
||||||
{#if !($page.data.streamed && $sortedDomains)}
|
{#if !($page.data.streamed && $sortedDomainsWithIntermediate)}
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="danger"
|
||||||
class="mt-3"
|
class="mt-3"
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
import type { Zone } from '$lib/model/zone';
|
import type { Zone } from '$lib/model/zone';
|
||||||
import { domains_idx } from '$lib/stores/domains';
|
import { domains_idx } from '$lib/stores/domains';
|
||||||
import { servicesSpecs, refreshServicesSpecs } from '$lib/stores/services';
|
import { servicesSpecs, refreshServicesSpecs } from '$lib/stores/services';
|
||||||
import { retrieveZone, sortedDomains, thisZone } from '$lib/stores/thiszone';
|
import { retrieveZone, sortedDomains, sortedDomainsWithIntermediate, thisZone } from '$lib/stores/thiszone';
|
||||||
import { t } from '$lib/translations';
|
import { t } from '$lib/translations';
|
||||||
|
|
||||||
if (!$servicesSpecs) refreshServicesSpecs();
|
if (!$servicesSpecs) refreshServicesSpecs();
|
||||||
|
@ -65,6 +65,7 @@
|
||||||
<SubdomainList
|
<SubdomainList
|
||||||
origin={data.domain}
|
origin={data.domain}
|
||||||
sortedDomains={$sortedDomains}
|
sortedDomains={$sortedDomains}
|
||||||
|
sortedDomainsWithIntermediate={$sortedDomainsWithIntermediate}
|
||||||
zone={$thisZone}
|
zone={$thisZone}
|
||||||
on:update-zone-services={(event) => thisZone.set(event.detail)}
|
on:update-zone-services={(event) => thisZone.set(event.detail)}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue