Compare commits
4 Commits
e491485773
...
d0b2de72fe
Author | SHA1 | Date | |
---|---|---|---|
d0b2de72fe | |||
58ab870afe | |||
3c6c070bd1 | |||
3211f8e6f9 |
@ -98,7 +98,7 @@
|
||||
</ModalFooter>
|
||||
{/if}
|
||||
<ModalFooter>
|
||||
{#if origin && zoneId}
|
||||
{#if update && origin && zoneId}
|
||||
<Button
|
||||
color="dark"
|
||||
outline={!showRecords}
|
||||
|
@ -38,6 +38,7 @@
|
||||
import type { Domain, DomainInList } from '$lib/model/domain';
|
||||
import type { ServiceCombined } from '$lib/model/service';
|
||||
import { ZoneViewGrid } from '$lib/model/usersettings';
|
||||
import { servicesSpecs } from '$lib/stores/services';
|
||||
import { userSession } from '$lib/stores/usersession';
|
||||
import { t } from '$lib/translations';
|
||||
|
||||
@ -82,7 +83,43 @@
|
||||
}
|
||||
</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}>
|
||||
<h2
|
||||
class="sticky-top"
|
||||
@ -188,12 +225,31 @@
|
||||
{/if}
|
||||
</span>
|
||||
</h2>
|
||||
{#if !showResources}
|
||||
<Badge
|
||||
id={"popoversvc-" + dn.replace('.', '__')}
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
{$t('domains.n-services', {count: services.length})}
|
||||
</Badge>
|
||||
<Popover
|
||||
dismissible
|
||||
placement="bottom"
|
||||
target={"popoversvc-" + dn.replace('.', '__')}
|
||||
>
|
||||
{#each services as service}
|
||||
<strong>{$servicesSpecs[service._svctype].name}:</strong>
|
||||
<span class="text-muted">{service._comment}</span>
|
||||
<br>
|
||||
{/each}
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if aliases.length != 0}
|
||||
<Badge
|
||||
id={"popoverbadge-" + dn.replace('.', '__')}
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
+ {$t('domains.n-aliases', {n: aliases.length})}
|
||||
+ {$t('domains.n-aliases', {count: aliases.length})}
|
||||
</Badge>
|
||||
<Popover
|
||||
dismissible
|
||||
@ -209,7 +265,7 @@
|
||||
{/each}
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if $userSession && $userSession.settings.zoneview !== ZoneViewGrid}
|
||||
{#if !showResources || ($userSession && $userSession.settings.zoneview !== ZoneViewGrid)}
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
@ -220,6 +276,7 @@
|
||||
{$t('domains.add-a-service')}
|
||||
</Button>
|
||||
{/if}
|
||||
{#if showResources}
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
@ -230,6 +287,7 @@
|
||||
<Icon name="link" />
|
||||
{$t('domains.add-an-alias')}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showResources}
|
||||
<div
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
export let origin: DomainInList | Domain;
|
||||
export let sortedDomains: Array<string>;
|
||||
export let sortedDomainsWithIntermediate: Array<string>;
|
||||
export let zone: Zone;
|
||||
|
||||
let aliases: Record<string, Array<string>>;
|
||||
@ -69,7 +70,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each sortedDomains as dn}
|
||||
{#each sortedDomainsWithIntermediate as dn}
|
||||
<SubdomainItem
|
||||
aliases={aliases[dn]?aliases[dn]:[]}
|
||||
{dn}
|
||||
|
41
ui/src/lib/components/domains/SubdomainListTiny.svelte
Normal file
41
ui/src/lib/components/domains/SubdomainListTiny.svelte
Normal file
@ -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}
|
@ -108,7 +108,8 @@
|
||||
"give-explicit-name": "Give an explicit name in order to easily find this service.",
|
||||
"history": "History",
|
||||
"list": "List importable domains",
|
||||
"n-aliases": "{{n:lt; 2:{{n}} alias; default:{{n}} aliases}}",
|
||||
"n-aliases": "{{count:lt; 2:{{count}} alias; default:{{count}} aliases}}",
|
||||
"n-services": "{{count:lt; 2:{{count}} service; default:{{count}} services}}",
|
||||
"please-fill-fields": "Please fill the following fields:",
|
||||
"removal": "Confirm Domain Removal",
|
||||
"save-modifications": "Save those modifications",
|
||||
|
@ -108,7 +108,8 @@
|
||||
"give-explicit-name": "Donnez un nom explicite afin de trouver facilement ce service.",
|
||||
"history": "Historique",
|
||||
"list": "Liste des domaines importables",
|
||||
"n-aliases": "{{n:lt; 2:{{n}} alias; default:{{n}} alias}}",
|
||||
"n-aliases": "{{count:lt; 2:{{count}} alias; default:{{count}} alias}}",
|
||||
"n-services": "{{count:lt; 2:{{count}} service; default:{{count}} services}}",
|
||||
"please-fill-fields": "Veuillez remplir les champs suivants :",
|
||||
"removal": "Confirmer la suppression du domaine",
|
||||
"save-modifications": "Enregistrer ces modifications",
|
||||
|
@ -101,7 +101,7 @@ export class ProviderForm {
|
||||
this.nextInProgress = true;
|
||||
this.saveState();
|
||||
if (this.form && this.form.nextButtonLink) {
|
||||
goto(this.form.nextButtonLink);
|
||||
window.location.href = this.form.nextButtonLink;
|
||||
} else {
|
||||
this.form = await this.changeState(this.form && this.form.nextButtonState ? this.form.nextButtonState : 0);
|
||||
}
|
||||
@ -112,7 +112,7 @@ export class ProviderForm {
|
||||
this.previousInProgress = true;
|
||||
this.saveState();
|
||||
if (this.form && this.form.previousButtonLink) {
|
||||
goto(this.form.previousButtonLink);
|
||||
window.location.href = this.form.previousButtonLink;
|
||||
} else {
|
||||
this.form = await this.changeState(this.form && this.form.previousButtonState ? this.form.previousButtonState : 0);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import { refreshDomains } from '$lib/stores/domains';
|
||||
|
||||
export const thisZone: Writable<null | Zone> = writable(null);
|
||||
|
||||
// sortedDomains returns all subdomains, sorted
|
||||
export const sortedDomains = derived(
|
||||
thisZone,
|
||||
($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) {
|
||||
thisZone.set(null);
|
||||
|
||||
|
@ -57,11 +57,12 @@
|
||||
import ModalViewZone, { controls as ctrlViewZone } from '$lib/components/ModalViewZone.svelte';
|
||||
import NewSubdomainPath, { controls as ctrlNewSubdomain } from '$lib/components/NewSubdomainPath.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 { ZoneMeta } from '$lib/model/zone';
|
||||
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';
|
||||
|
||||
export let data: {domain: DomainInList;};
|
||||
@ -175,7 +176,7 @@
|
||||
<Icon name="chevron-left" />
|
||||
Retour à la zone
|
||||
</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">
|
||||
<Button
|
||||
type="button"
|
||||
@ -250,16 +251,17 @@
|
||||
</div>
|
||||
{#await $page.data.streamed.zone then z}
|
||||
<div style="min-height:0; overflow-y: auto;">
|
||||
{#each $sortedDomains as dn}
|
||||
<a
|
||||
href={'#' + (dn?dn:'@')}
|
||||
title={fqdn(dn, data.domain.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, data.domain.domain)}
|
||||
</a>
|
||||
{/each}
|
||||
{#if isReverseZone(data.domain.domain)}
|
||||
<SubdomainListTiny
|
||||
domains={$sortedDomains}
|
||||
origin={data.domain}
|
||||
/>
|
||||
{:else}
|
||||
<SubdomainListTiny
|
||||
domains={$sortedDomainsWithIntermediate}
|
||||
origin={data.domain}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
@ -267,7 +269,7 @@
|
||||
<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.streamed && $sortedDomains)}
|
||||
{#if !($page.data.streamed && $sortedDomainsWithIntermediate)}
|
||||
<Button
|
||||
color="danger"
|
||||
class="mt-3"
|
||||
|
@ -35,7 +35,7 @@
|
||||
import type { Zone } from '$lib/model/zone';
|
||||
import { domains_idx } from '$lib/stores/domains';
|
||||
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';
|
||||
|
||||
if (!$servicesSpecs) refreshServicesSpecs();
|
||||
@ -65,6 +65,7 @@
|
||||
<SubdomainList
|
||||
origin={data.domain}
|
||||
sortedDomains={$sortedDomains}
|
||||
sortedDomainsWithIntermediate={$sortedDomainsWithIntermediate}
|
||||
zone={$thisZone}
|
||||
on:update-zone-services={(event) => thisZone.set(event.detail)}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user