Refactor SubdomainItem: split Header
This commit is contained in:
parent
002b944532
commit
d30baef2bd
8 changed files with 295 additions and 302 deletions
|
|
@ -56,6 +56,7 @@
|
|||
"resolver": "Resolver",
|
||||
"run": "Run the request!",
|
||||
"survey": "A remark? A comment to share? Don't hesitate to write to us!",
|
||||
"spinning": "Spinning",
|
||||
"update": "Update",
|
||||
"welcome": {
|
||||
"start": "Welcome to ",
|
||||
|
|
@ -89,7 +90,7 @@
|
|||
"add-a-subdomain": "Add a subdomain",
|
||||
"add-a-service": "Add a service",
|
||||
"add-alias": "Add alias",
|
||||
"add-an-alias": "Add an alias",
|
||||
"add-an-alias": "Add an alias to {{domain}}",
|
||||
"add-now": "Add it now!",
|
||||
"create-on-provider": "Create on {{provider}}",
|
||||
"added-success": "Great! {{domain}} has been added. You can manage it right now.",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
"rename": "Renommer",
|
||||
"resolver": "Résolveur",
|
||||
"run": "Lancer l'opération !",
|
||||
"spinning": "Chargement",
|
||||
"update": "Mettre à jour",
|
||||
"help": "Besoin d'aide ?",
|
||||
"records": "{{n:eq; 0:aucun enregistrement {{type}}; 1:enregistrement {{type}}; default:enregistrements {{type}}}}",
|
||||
|
|
@ -85,7 +86,7 @@
|
|||
"add-a-subdomain": "Ajouter un sous-domaine",
|
||||
"add-a-service": "Ajouter un service",
|
||||
"add-alias": "Ajout d'alias",
|
||||
"add-an-alias": "Ajouter un alias",
|
||||
"add-an-alias": "Ajouter un alias vers {{domain}}",
|
||||
"add-now": "Ajouter maintenant !",
|
||||
"added-success": "Bravo ! {{domain}} a été ajouté. Vous pouvez désormais le gérer.",
|
||||
"apply": {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,33 @@ import { refreshDomains } from "$lib/stores/domains";
|
|||
|
||||
export const thisZone: Writable<null | Zone> = writable(null);
|
||||
|
||||
// thisAliases returns all aliases in the domain
|
||||
export const thisAliases = derived(thisZone, (zone: null | Zone) => {
|
||||
const aliases: Record<string, Array<string>> = {};
|
||||
|
||||
if (!zone || !zone.services) {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
for (const dn in zone.services) {
|
||||
if (!zone.services[dn]) continue;
|
||||
|
||||
zone.services[dn].forEach(function (svc) {
|
||||
if (svc._svctype === "svcs.CNAME") {
|
||||
if (!aliases[svc.Service.Target]) {
|
||||
aliases[svc.Service.Target] = [];
|
||||
}
|
||||
aliases[svc.Service.Target].push(dn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (aliases["@"])
|
||||
aliases[""] = aliases["@"];
|
||||
|
||||
return aliases;
|
||||
});
|
||||
|
||||
// sortedDomains returns all subdomains, sorted
|
||||
export const sortedDomains = derived(thisZone, ($thisZone: null | Zone) => {
|
||||
if (!$thisZone) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
<script lang="ts">
|
||||
import { Button, Col, Icon, Row, Spinner } from "@sveltestrap/sveltestrap";
|
||||
|
||||
import AliasModal from "./AliasModal.svelte";
|
||||
import SubdomainList from "./SubdomainList.svelte";
|
||||
import type { Domain } from "$lib/model/domain";
|
||||
import type { Zone } from "$lib/model/zone";
|
||||
|
|
@ -57,19 +58,17 @@
|
|||
{#if !$sortedDomainsWithIntermediate || $sortedDomains.length == 0}
|
||||
<SubdomainList
|
||||
origin={data.domain}
|
||||
sortedDomains={['']}
|
||||
sortedDomainsWithIntermediate={['']}
|
||||
zone={$thisZone}
|
||||
on:update-zone-services={(event) => thisZone.set(event.detail)}
|
||||
/>
|
||||
{:else}
|
||||
<SubdomainList
|
||||
origin={data.domain}
|
||||
sortedDomains={$sortedDomains}
|
||||
sortedDomainsWithIntermediate={$sortedDomainsWithIntermediate}
|
||||
zone={$thisZone}
|
||||
on:update-zone-services={(event) => thisZone.set(event.detail)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<AliasModal
|
||||
origin={data.domain}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
import { fqdn, validateDomain } from "$lib/dns";
|
||||
import type { Domain } from "$lib/model/domain";
|
||||
import type { Zone } from "$lib/model/zone";
|
||||
import { thisZone } from "$lib/stores/thiszone";
|
||||
import { t } from "$lib/translations";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
|
@ -64,7 +65,7 @@
|
|||
export let dn: string = "";
|
||||
export let origin: Domain;
|
||||
export let value: string = "";
|
||||
export let zone: Zone;
|
||||
let zone = $thisZone;
|
||||
|
||||
let newDomainState: boolean | undefined = undefined;
|
||||
$: newDomainState = value ? validateNewSubdomain(value) : undefined;
|
||||
|
|
@ -120,7 +121,7 @@
|
|||
Service: { Target: dn ? dn : "@" },
|
||||
}).then(
|
||||
(z) => {
|
||||
dispatch("update-zone-services", z);
|
||||
thisZone.set(z);
|
||||
addAliasInProgress = false;
|
||||
toggle();
|
||||
},
|
||||
|
|
@ -142,8 +143,7 @@
|
|||
|
||||
<Modal {isOpen} {toggle}>
|
||||
<ModalHeader {toggle}>
|
||||
{$t("domains.add-an-alias")}
|
||||
{origin.domain}
|
||||
{$t("domains.add-an-alias", {domain: origin.domain})}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<form id="addAliasForm" on:submit|preventDefault={submitAliasForm}>
|
||||
|
|
|
|||
|
|
@ -24,267 +24,72 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { Badge, Button, Icon, Popover, Spinner } from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { deleteZoneService } from "$lib/api/zone";
|
||||
import { controls as ctrlNewService } from "$lib/components/services/NewServicePath.svelte";
|
||||
import { controls as ctrlService } from "$lib/components/services/ServiceModal.svelte";
|
||||
import Service from "./Service.svelte";
|
||||
import { fqdn, isReverseZone, unreverseDomain } from "$lib/dns";
|
||||
import SubdomainItemHeader from "./SubdomainItemHeader.svelte";
|
||||
import { isReverseZone } from "$lib/dns";
|
||||
import type { Domain } from "$lib/model/domain";
|
||||
import type { ServiceCombined } from "$lib/model/service";
|
||||
import { ZoneViewGrid } from "$lib/model/usersettings";
|
||||
import { servicesSpecs } from "$lib/stores/services";
|
||||
import { thisZone } from "$lib/stores/thiszone";
|
||||
import { userSession } from "$lib/stores/usersession";
|
||||
import { t } from "$lib/translations";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let aliases: Array<string> = [];
|
||||
export let dn: string;
|
||||
export let origin: Domain;
|
||||
export let services: Array<ServiceCombined>;
|
||||
export let zoneId: string;
|
||||
|
||||
let reverseZone = false;
|
||||
$: reverseZone = isReverseZone(origin.domain);
|
||||
|
||||
let showResources = true;
|
||||
let showResources = true && (services.length > 1 || (services.length === 1 && services[0]._svctype !== "svcs.CNAME" && services[0]._svctype !== "svcs.PTR"));
|
||||
|
||||
function isCNAME(services: Array<ServiceCombined>) {
|
||||
return services.length === 1 && services[0]._svctype === "svcs.CNAME";
|
||||
}
|
||||
|
||||
function isPTR(services: Array<ServiceCombined>) {
|
||||
return services.length === 1 && services[0]._svctype === "svcs.PTR";
|
||||
}
|
||||
|
||||
let deleteServiceInProgress = false;
|
||||
function deleteCNAME() {
|
||||
deleteServiceInProgress = true;
|
||||
deleteZoneService(origin, zoneId, services[0]).then(
|
||||
(z) => {
|
||||
dispatch("update-zone-services", z);
|
||||
deleteServiceInProgress = false;
|
||||
},
|
||||
(err) => {
|
||||
deleteServiceInProgress = false;
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function showServiceModal(service: ServiceCombined) {
|
||||
dispatch("show-service", service);
|
||||
function showServiceModal(event: CustomEvent<ServiceCombined>) {
|
||||
ctrlService.Open(event.detail);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if services.length === 0 && dn != ""}
|
||||
<div id={dn}>
|
||||
{#if !reverseZone}
|
||||
<h2 class="sticky-top bg-light d-flex align-items-center" style="z-index: 1">
|
||||
<span class="text-truncate text-muted">
|
||||
<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>
|
||||
<div class="flex-fill"></div>
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
size="sm"
|
||||
class="ms-2"
|
||||
title={$t("service.add")}
|
||||
on:click={() => dispatch("new-service")}
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</Button>
|
||||
</h2>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if isCNAME(services) || isPTR(services)}
|
||||
<div id={dn}>
|
||||
<h2 class="sticky-top bg-light d-flex align-items-center" style="z-index: 1">
|
||||
<span class="text-truncate">
|
||||
{#if isPTR(services)}
|
||||
<Icon name="signpost" title="PTR" />
|
||||
{:else}
|
||||
<Icon name="sign-turn-right" title="CNAME" />
|
||||
{/if}
|
||||
<span class="font-monospace" title={fqdn(dn, origin.domain)}>
|
||||
{#if reverseZone}
|
||||
{unreverseDomain(fqdn(dn, origin.domain))}
|
||||
{:else}
|
||||
{fqdn(dn, origin.domain)}
|
||||
{/if}
|
||||
</span>
|
||||
</span>
|
||||
<span class="text-truncate">
|
||||
<Icon name="arrow-right" />
|
||||
<span class="font-monospace" title={services[0].Service.Target}>
|
||||
{services[0].Service.Target}
|
||||
</span>
|
||||
</span>
|
||||
<div class="flex-fill"></div>
|
||||
<Button
|
||||
type="button"
|
||||
color="info"
|
||||
outline
|
||||
size="sm"
|
||||
class="ms-2"
|
||||
title={$t("domains.edit-target")}
|
||||
on:click={() => showServiceModal(services[0])}
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="danger"
|
||||
disabled={deleteServiceInProgress}
|
||||
outline
|
||||
size="sm"
|
||||
class="ms-2"
|
||||
title={isPTR(services) ? $t("domains.drop-pointer") : $t("domains.drop-alias")}
|
||||
on:click={deleteCNAME}
|
||||
>
|
||||
{#if deleteServiceInProgress}
|
||||
<Spinner size="sm" />
|
||||
{:else}
|
||||
<Icon name="x-circle" />
|
||||
{/if}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
size="sm"
|
||||
class="ms-2"
|
||||
title={$t("service.add")}
|
||||
on:click={() => dispatch("new-service")}
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</Button>
|
||||
</h2>
|
||||
</div>
|
||||
{:else}
|
||||
<div id={dn ? dn : "@"}>
|
||||
<div class="d-flex align-items-center sticky-top mb-2 gap-2 bg-light" style="z-index: 1">
|
||||
<h2
|
||||
role="button"
|
||||
tabindex="0"
|
||||
style="white-space: nowrap; cursor: pointer;"
|
||||
class="mb-0 text-truncate"
|
||||
on:click={() => (showResources = !showResources)}
|
||||
on:keypress={() => (showResources = !showResources)}
|
||||
>
|
||||
{#if showResources}
|
||||
<Icon name="chevron-down" />
|
||||
{:else}
|
||||
<Icon name="chevron-right" />
|
||||
{/if}
|
||||
<span class="font-monospace" title={fqdn(dn, origin.domain)}>
|
||||
{#if reverseZone}
|
||||
{unreverseDomain(fqdn(dn, origin.domain))}
|
||||
{:else}
|
||||
{fqdn(dn, origin.domain)}
|
||||
{/if}
|
||||
</span>
|
||||
</h2>
|
||||
{#if !showResources && $servicesSpecs}
|
||||
<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", { count: aliases.length })}
|
||||
</Badge>
|
||||
<Popover
|
||||
dismissible
|
||||
placement="bottom"
|
||||
target={"popoverbadge-" + dn.replace(".", "__")}
|
||||
class="font-monospace"
|
||||
>
|
||||
{#each aliases as alias}
|
||||
<a href={"#" + alias}>
|
||||
{alias}
|
||||
</a>
|
||||
<br />
|
||||
{/each}
|
||||
</Popover>
|
||||
{/if}
|
||||
<div class="flex-fill"></div>
|
||||
{#if !showResources || ($userSession && $userSession.settings.zoneview !== ZoneViewGrid)}
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
size="sm"
|
||||
title={$t("domains.add-a-service")}
|
||||
on:click={() => dispatch("new-service")}
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</Button>
|
||||
{/if}
|
||||
{#if showResources}
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
outline
|
||||
size="sm"
|
||||
title={$t("domains.add-an-alias")}
|
||||
on:click={() => dispatch("new-alias")}
|
||||
>
|
||||
<Icon name="link" />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if showResources}
|
||||
<div
|
||||
class:d-flex={showResources &&
|
||||
$userSession &&
|
||||
$userSession.settings.zoneview === ZoneViewGrid}
|
||||
class:justify-content-around={showResources &&
|
||||
$userSession &&
|
||||
$userSession.settings.zoneview === ZoneViewGrid}
|
||||
class:flex-wrap={showResources &&
|
||||
$userSession &&
|
||||
$userSession.settings.zoneview === ZoneViewGrid}
|
||||
>
|
||||
{#each services as service}
|
||||
{#key service}
|
||||
<Service
|
||||
{origin}
|
||||
{service}
|
||||
{zoneId}
|
||||
on:show-service={(event) => showServiceModal(event.detail)}
|
||||
on:update-zone-services={(event) =>
|
||||
dispatch("update-zone-services", event.detail)}
|
||||
/>
|
||||
{/key}
|
||||
{/each}
|
||||
{#if $userSession && $userSession.settings.zoneview === ZoneViewGrid}
|
||||
{#if $thisZone}
|
||||
<div id={dn ? dn : "@"}>
|
||||
<SubdomainItemHeader
|
||||
{dn}
|
||||
{origin}
|
||||
{services}
|
||||
zoneId={$thisZone.id}
|
||||
{reverseZone}
|
||||
bind:showResources={showResources}
|
||||
/>
|
||||
{#if showResources}
|
||||
<div
|
||||
class:d-flex={showResources &&
|
||||
$userSession &&
|
||||
$userSession.settings.zoneview === ZoneViewGrid}
|
||||
class:justify-content-around={showResources &&
|
||||
$userSession &&
|
||||
$userSession.settings.zoneview === ZoneViewGrid}
|
||||
class:flex-wrap={showResources &&
|
||||
$userSession &&
|
||||
$userSession.settings.zoneview === ZoneViewGrid}
|
||||
>
|
||||
{#each services as service}
|
||||
{#key service}
|
||||
<Service
|
||||
{origin}
|
||||
{zoneId}
|
||||
on:show-service={() => dispatch("new-service")}
|
||||
on:update-zone-services={(event) =>
|
||||
dispatch("update-zone-services", event.detail)}
|
||||
{service}
|
||||
zoneId={$thisZone.id}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
{/each}
|
||||
{#if $userSession && $userSession.settings.zoneview === ZoneViewGrid}
|
||||
<Service
|
||||
{origin}
|
||||
zoneId={$thisZone.id}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2025 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 { Badge, Button, Icon, Popover, Spinner } from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { deleteZoneService } from "$lib/api/zone";
|
||||
import { controls as ctrlNewAlias } from "./AliasModal.svelte";
|
||||
import { controls as ctrlNewService } from "$lib/components/services/NewServicePath.svelte";
|
||||
import { controls as ctrlService } from "$lib/components/services/ServiceModal.svelte";
|
||||
import { fqdn, unreverseDomain } from "$lib/dns";
|
||||
import type { Domain } from "$lib/model/domain";
|
||||
import type { ServiceCombined } from "$lib/model/service";
|
||||
import { ZoneViewGrid } from "$lib/model/usersettings";
|
||||
import { servicesSpecs } from "$lib/stores/services";
|
||||
import { thisAliases, thisZone } from "$lib/stores/thiszone";
|
||||
import { userSession } from "$lib/stores/usersession";
|
||||
import { t } from "$lib/translations";
|
||||
|
||||
export let dn: string;
|
||||
export let origin: Domain;
|
||||
export let services: Array<ServiceCombined>;
|
||||
export let zoneId: string;
|
||||
export let reverseZone = false;
|
||||
export let showResources = true;
|
||||
|
||||
function isCNAME(services: Array<ServiceCombined>) {
|
||||
return services.length === 1 && services[0]._svctype === "svcs.CNAME";
|
||||
}
|
||||
|
||||
function isPTR(services: Array<ServiceCombined>) {
|
||||
return services.length === 1 && services[0]._svctype === "svcs.PTR";
|
||||
}
|
||||
|
||||
let deleteServiceInProgress = false;
|
||||
function deleteCNAME() {
|
||||
deleteServiceInProgress = true;
|
||||
deleteZoneService(origin, zoneId, services[0]).then(
|
||||
(z) => {
|
||||
thisZone.set(z);
|
||||
deleteServiceInProgress = false;
|
||||
},
|
||||
(err) => {
|
||||
deleteServiceInProgress = false;
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function showServiceModal(service: ServiceCombined) {
|
||||
ctrlService.Open(service);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="sticky-top bg-light d-flex align-items-center mb-2 gap-2"
|
||||
style="z-index: 1"
|
||||
>
|
||||
<h2
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class="text-truncate"
|
||||
class:text-muted={services.length === 0 && dn != ""}
|
||||
style:cursor={(services.length || dn == "") && !isPTR(services) && !isCNAME(services) ? "pointer": "default"}
|
||||
on:click={() => (showResources = !showResources)}
|
||||
on:keypress={() => (showResources = !showResources)}
|
||||
>
|
||||
{#if services.length === 0 && dn != ""}
|
||||
<Icon name="plus-square-dotted" title="Intermediate domain with no service" />
|
||||
{:else if isPTR(services)}
|
||||
<Icon name="signpost" title="PTR" />
|
||||
{:else if isCNAME(services)}
|
||||
<Icon name="sign-turn-right" title="CNAME" />
|
||||
{:else if showResources}
|
||||
<Icon name="chevron-down" />
|
||||
{:else}
|
||||
<Icon name="chevron-right" />
|
||||
{/if}
|
||||
<span class="font-monospace" title={fqdn(dn, origin.domain)}>
|
||||
{#if reverseZone}
|
||||
{unreverseDomain(fqdn(dn, origin.domain))}
|
||||
{:else}
|
||||
{fqdn(dn, origin.domain)}
|
||||
{/if}
|
||||
</span>
|
||||
</h2>
|
||||
{#if isCNAME(services) || isPTR(services)}
|
||||
<span class="text-truncate text-muted lead">
|
||||
<Icon name="arrow-right" />
|
||||
<span class="font-monospace">
|
||||
{services[0].Service.Target}
|
||||
</span>
|
||||
</span>
|
||||
{:else if !showResources && services.length}
|
||||
<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}
|
||||
{#if $servicesSpecs && $servicesSpecs[service._svctype]}
|
||||
<strong>{$servicesSpecs[service._svctype].name}:</strong>
|
||||
{/if}
|
||||
<span class="text-muted">{service._comment}</span>
|
||||
<br />
|
||||
{/each}
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if $thisAliases[dn] && $thisAliases[dn].length != 0}
|
||||
<Badge id={"popoverbadge-" + dn.replace(".", "__")} style="cursor: pointer;">
|
||||
+ {$t("domains.n-aliases", { count: $thisAliases[dn].length })}
|
||||
</Badge>
|
||||
<Popover
|
||||
dismissible
|
||||
placement="bottom"
|
||||
target={"popoverbadge-" + dn.replace(".", "__")}
|
||||
class="font-monospace"
|
||||
>
|
||||
{#each $thisAliases[dn] as alias}
|
||||
<a href={"#" + alias}>
|
||||
{alias}
|
||||
</a>
|
||||
<br />
|
||||
{/each}
|
||||
</Popover>
|
||||
{/if}
|
||||
<div class="flex-fill"></div>
|
||||
{#if isCNAME(services) || isPTR(services)}
|
||||
<Button
|
||||
type="button"
|
||||
color="info"
|
||||
outline
|
||||
size="sm"
|
||||
title={$t("domains.edit-target")}
|
||||
on:click={() => showServiceModal(services[0])}
|
||||
>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="danger"
|
||||
disabled={deleteServiceInProgress}
|
||||
outline
|
||||
size="sm"
|
||||
title={isPTR(services) ? $t("domains.drop-pointer") : $t("domains.drop-alias")}
|
||||
on:click={deleteCNAME}
|
||||
>
|
||||
{#if deleteServiceInProgress}
|
||||
<Spinner size="sm" />
|
||||
{:else}
|
||||
<Icon name="x-circle" />
|
||||
{/if}
|
||||
</Button>
|
||||
{:else if showResources && services.length}
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
outline
|
||||
size="sm"
|
||||
title={$t("domains.add-an-alias")}
|
||||
on:click={() => ctrlNewAlias.Open(dn)}
|
||||
>
|
||||
<Icon name="link" />
|
||||
</Button>
|
||||
{/if}
|
||||
{#if !showResources || ($userSession && $userSession.settings.zoneview !== ZoneViewGrid)}
|
||||
<Button
|
||||
type="button"
|
||||
color="primary"
|
||||
size="sm"
|
||||
title={$t("domains.add-a-service")}
|
||||
on:click={() => ctrlNewService.Open(dn)}
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -24,63 +24,23 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import AliasModal, { controls as ctrlAlias } from "./AliasModal.svelte";
|
||||
import { controls as ctrlNewService } from "$lib/components/services/NewServicePath.svelte";
|
||||
import { controls as ctrlService } from "$lib/components/services/ServiceModal.svelte";
|
||||
import SubdomainItem from "./SubdomainItem.svelte";
|
||||
import type { Domain } from "$lib/model/domain";
|
||||
import type { ServiceCombined } from "$lib/model/service";
|
||||
import type { Zone } from "$lib/model/zone";
|
||||
import { thisZone } from "$lib/stores/thiszone";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let origin: Domain;
|
||||
export let sortedDomains: Array<string>;
|
||||
export let sortedDomainsWithIntermediate: Array<string>;
|
||||
export let zone: Zone;
|
||||
|
||||
let aliases: Record<string, Array<string>>;
|
||||
$: {
|
||||
const tmp: Record<string, Array<string>> = {};
|
||||
|
||||
for (const dn of sortedDomains) {
|
||||
if (!zone.services[dn]) continue;
|
||||
|
||||
zone.services[dn].forEach(function (svc) {
|
||||
if (svc._svctype === "svcs.CNAME") {
|
||||
if (!tmp[svc.Service.Target]) {
|
||||
tmp[svc.Service.Target] = [];
|
||||
}
|
||||
tmp[svc.Service.Target].push(dn);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (tmp["@"]) tmp[""] = tmp["@"];
|
||||
|
||||
aliases = tmp;
|
||||
}
|
||||
|
||||
function showServiceModal(event: CustomEvent<ServiceCombined>) {
|
||||
ctrlService.Open(event.detail);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each sortedDomainsWithIntermediate as dn}
|
||||
<SubdomainItem
|
||||
aliases={aliases[dn] ? aliases[dn] : []}
|
||||
{dn}
|
||||
{origin}
|
||||
zoneId={zone.id}
|
||||
services={zone.services[dn] ? zone.services[dn] : []}
|
||||
on:new-alias={() => ctrlAlias.Open(dn)}
|
||||
on:new-service={() => ctrlNewService.Open(dn)}
|
||||
on:show-service={showServiceModal}
|
||||
on:update-zone-services={(event) => dispatch("update-zone-services", event.detail)}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<AliasModal
|
||||
{origin}
|
||||
{zone}
|
||||
on:update-zone-services={(event) => dispatch("update-zone-services", event.detail)}
|
||||
/>
|
||||
{#if $thisZone}
|
||||
{#each sortedDomainsWithIntermediate as dn}
|
||||
<SubdomainItem
|
||||
{dn}
|
||||
{origin}
|
||||
services={$thisZone.services[dn] ? $thisZone.services[dn] : []}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue