web-admin: Add domains pages
This commit is contained in:
parent
42c36db56b
commit
abe58144b2
9 changed files with 862 additions and 5 deletions
|
|
@ -26,14 +26,16 @@
|
|||
Container,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { getUsers } from '$lib/api-admin';
|
||||
import { getDomains, getUsers } from '$lib/api-admin';
|
||||
import DatabaseBackupCard from "./DatabaseBackupCard.svelte";
|
||||
|
||||
let totalUsers: number | undefined = $state();
|
||||
getUsers().then((res) => { totalUsers = res.data?.length || 0; });
|
||||
|
||||
let totalDomains: number | undefined = $state();
|
||||
getDomains().then((res) => { totalDomains = res.data?.length || 0; });
|
||||
|
||||
let stats = {
|
||||
totalDomains: 0,
|
||||
activeProviders: 0
|
||||
};
|
||||
</script>
|
||||
|
|
@ -72,7 +74,7 @@
|
|||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-1">Total Domains</h6>
|
||||
<h2 class="mb-0">{stats.totalDomains}</h2>
|
||||
<h2 class="mb-0">{totalDomains}</h2>
|
||||
</div>
|
||||
<div class="text-primary">
|
||||
<i class="bi bi-globe" style="font-size: 2rem;"></i>
|
||||
|
|
|
|||
141
web-admin/src/routes/domains/+page.svelte
Normal file
141
web-admin/src/routes/domains/+page.svelte
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2026 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 {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Container,
|
||||
Icon,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputGroupText,
|
||||
Table,
|
||||
Row,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { getDomains, deleteDomainsByDomain } from '$lib/api-admin';
|
||||
import { toasts } from '$lib/stores/toasts';
|
||||
|
||||
let domainsQ = $state(getDomains());
|
||||
|
||||
let searchQuery = $state('');
|
||||
|
||||
async function handleDeleteDomain(domainId: string, domainName: string) {
|
||||
if (confirm(`Are you sure you want to delete domain "${domainName}"?`)) {
|
||||
try {
|
||||
await deleteDomainsByDomain({ path: { domain: domainId } });
|
||||
// Refresh the domains list
|
||||
domainsQ = getDomains();
|
||||
toasts.addToast({
|
||||
message: `Domain "${domainName}" has been deleted successfully`,
|
||||
type: 'success',
|
||||
timeout: 5000,
|
||||
});
|
||||
} catch (error) {
|
||||
toasts.addErrorToast({
|
||||
message: 'Failed to delete domain: ' + error,
|
||||
timeout: 10000,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container class="flex-fill my-5">
|
||||
<Row class="mb-4">
|
||||
<Col md={8}>
|
||||
<h1 class="display-5">
|
||||
<Icon name="globe"></Icon>
|
||||
Domain Management
|
||||
</h1>
|
||||
<p class="d-flex gap-3 align-items-center text-muted">
|
||||
<span class="lead">
|
||||
Manage all domains
|
||||
</span>
|
||||
{#await domainsQ then domainsR}
|
||||
<span>Total: {domainsR.data?.length ?? 0} domains</span>
|
||||
{/await}
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row class="mb-4">
|
||||
<Col md={8} lg={6}>
|
||||
<InputGroup>
|
||||
<InputGroupText>
|
||||
<Icon name="search"></Icon>
|
||||
</InputGroupText>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Search domains..."
|
||||
bind:value={searchQuery}
|
||||
/>
|
||||
</InputGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{#await domainsQ}
|
||||
Please wait...
|
||||
{:then domainsR}
|
||||
{@const domains = domainsR.data ?? []}
|
||||
<div class="table-responsive">
|
||||
<Table hover bordered>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Domain Name</th>
|
||||
<th>Group</th>
|
||||
<th>Owner ID</th>
|
||||
<th>Provider ID</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each domains.filter(d =>
|
||||
(d.id && d.id.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
|
||||
(d.domain && d.domain.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
|
||||
(d.group && d.group.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1)
|
||||
) as domain}
|
||||
<tr>
|
||||
<td>{domain.id}</td>
|
||||
<td>{domain.domain}</td>
|
||||
<td>{domain.group || '-'}</td>
|
||||
<td>{domain.id_owner || '-'}</td>
|
||||
<td>{domain.id_provider || '-'}</td>
|
||||
<td class="d-flex flex-nowrap gap-1">
|
||||
<Button color="primary" outline size="sm" href="/users/{domain.id_owner}/domains/{domain.id}">
|
||||
<Icon name="pencil"></Icon>
|
||||
</Button>
|
||||
<Button color="primary" outline size="sm" onclick={() => handleDeleteDomain(domain.id || '', domain.domain || '')}>
|
||||
<Icon name="trash"></Icon>
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
{/await}
|
||||
</Container>
|
||||
43
web-admin/src/routes/domains/[domain]/+page.ts
Normal file
43
web-admin/src/routes/domains/[domain]/+page.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2022-2026 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/>.
|
||||
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { getDomains } from '$lib/api-admin';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const domainId = params.domain;
|
||||
|
||||
// Fetch all domains to find the owner
|
||||
const domainsResponse = await getDomains();
|
||||
|
||||
if (domainsResponse.data) {
|
||||
const domain = domainsResponse.data.find(d => d.id === domainId);
|
||||
|
||||
if (domain && domain.id_owner) {
|
||||
// Redirect to the user-specific domain route
|
||||
throw redirect(302, `/users/${domain.id_owner}/domains/${domainId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If domain not found or no owner, throw 404
|
||||
throw redirect(302, '/domains');
|
||||
};
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
import { page } from '$app/stores';
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
Button,
|
||||
Col,
|
||||
Container,
|
||||
|
|
@ -33,12 +34,13 @@
|
|||
Spinner,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { getUsersByUid, type HappydnsDomain } from '$lib/api-admin';
|
||||
import { client } from '$lib/api-admin/client.gen';
|
||||
import { getUsersByUid, getUsersByUidDomains } from '$lib/api-admin';
|
||||
import UserInfoCard from './UserInfoCard.svelte';
|
||||
import UserDomainsCard from './domains/UserDomainsCard.svelte';
|
||||
|
||||
const uid = $page.params.uid!;
|
||||
let userQ = $state(getUsersByUid({ path: { uid } }));
|
||||
let domainsQ = $state(getUsersByUidDomains({ path: { uid } }));
|
||||
</script>
|
||||
|
||||
<Container class="flex-fill my-5">
|
||||
|
|
@ -63,6 +65,10 @@
|
|||
<Col md={8} lg={6}>
|
||||
<UserInfoCard {user} {uid} />
|
||||
</Col>
|
||||
|
||||
<Col md={8} lg={6}>
|
||||
<UserDomainsCard {domainsQ} userId={user.id!} />
|
||||
</Col>
|
||||
</Row>
|
||||
{:else}
|
||||
<Alert color="warning">
|
||||
|
|
|
|||
55
web-admin/src/routes/users/[uid]/domains/+page.svelte
Normal file
55
web-admin/src/routes/users/[uid]/domains/+page.svelte
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2026 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 { page } from '$app/stores';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Container,
|
||||
Icon,
|
||||
Row,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
import { getUsersByUidDomains } from '$lib/api-admin';
|
||||
import UserDomainsCard from './UserDomainsCard.svelte';
|
||||
|
||||
let userId = $derived($page.params.uid!);
|
||||
let domainsQ = $derived(getUsersByUidDomains({ path: { uid: userId }}));
|
||||
</script>
|
||||
|
||||
<Container class="flex-fill my-5">
|
||||
<Row class="mb-4">
|
||||
<Col>
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<Button color="link" href="/users/{userId}" class="text-black">
|
||||
<Icon name="chevron-left"></Icon>
|
||||
</Button>
|
||||
<h1 class="display-5 mb-0">
|
||||
User Domains
|
||||
</h1>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<UserDomainsCard {domainsQ} {userId} />
|
||||
</Container>
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2026 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 {
|
||||
Alert,
|
||||
Badge,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Icon,
|
||||
ListGroup,
|
||||
ListGroupItem,
|
||||
Spinner,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import type { HappydnsDomain, HappydnsErrorResponse } from '$lib/api-admin';
|
||||
|
||||
interface UserDomainsCardProps {
|
||||
domainsQ: Promise<(
|
||||
| { data: HappydnsDomain[]; error: undefined }
|
||||
| { data: undefined; error: HappydnsErrorResponse }
|
||||
) & { request: Request; response: Response }>;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
let { domainsQ, userId }: UserDomainsCardProps = $props();
|
||||
</script>
|
||||
|
||||
{#await domainsQ}
|
||||
<Card>
|
||||
<CardBody>
|
||||
<div class="text-center">
|
||||
<Spinner color="primary" size="sm" />
|
||||
<span class="ms-2">Loading domains...</span>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
{:then domainsR}
|
||||
{@const userDomains = domainsR.data || []}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
<Icon name="globe"></Icon>
|
||||
User Domains
|
||||
</h5>
|
||||
<Badge color="secondary">{userDomains.length} domains</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
{#if userDomains.length === 0}
|
||||
<CardBody>
|
||||
<p class="text-muted mb-0">This user has no domains.</p>
|
||||
</CardBody>
|
||||
{:else}
|
||||
<ListGroup flush>
|
||||
{#each userDomains as domain}
|
||||
<ListGroupItem href="/users/{userId}/domains/{domain.id}" action>
|
||||
<strong>{domain.domain}</strong>
|
||||
{#if domain.group}
|
||||
<Badge color="info" class="ms-2">{domain.group}</Badge>
|
||||
{/if}
|
||||
<div class="small text-muted">
|
||||
<code>{domain.id}</code>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
{/each}
|
||||
</ListGroup>
|
||||
{/if}
|
||||
</Card>
|
||||
{:catch}
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Alert color="warning" class="mb-0">
|
||||
Unable to load domains.
|
||||
</Alert>
|
||||
</CardBody>
|
||||
</Card>
|
||||
{/await}
|
||||
109
web-admin/src/routes/users/[uid]/domains/[domain]/+page.svelte
Normal file
109
web-admin/src/routes/users/[uid]/domains/[domain]/+page.svelte
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2026 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 { page } from '$app/stores';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Col,
|
||||
Container,
|
||||
Icon,
|
||||
Row,
|
||||
Spinner,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { getUsersByUidDomainsByDomain } from '$lib/api-admin';
|
||||
import DomainInformationCard from './DomainInformationCard.svelte';
|
||||
import ZoneHistoryCard from './zones/ZoneHistoryCard.svelte';
|
||||
|
||||
const uid = $derived($page.params.uid!);
|
||||
const domainId = $derived($page.params.domain!);
|
||||
let domainQ = $derived(getUsersByUidDomainsByDomain({ path: { uid, domain: domainId } }));
|
||||
|
||||
let zoneHistory = $state<string[]>([]);
|
||||
|
||||
// Load domain data when promise resolves
|
||||
$effect(() => {
|
||||
domainQ.then(response => {
|
||||
if (response?.data && response.data.length > 0) {
|
||||
zoneHistory = response.data[0].zone_history || [];
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<Container class="flex-fill my-5">
|
||||
<Row class="mb-4">
|
||||
<Col>
|
||||
<h1 class="display-5">
|
||||
<Icon name="pencil"></Icon>
|
||||
Edit Domain
|
||||
</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{#await domainQ}
|
||||
<div class="text-center my-5">
|
||||
<Spinner color="primary" />
|
||||
<p class="mt-3">Loading domain...</p>
|
||||
</div>
|
||||
{:then domainR}
|
||||
{#if domainR?.data && domainR.data.length > 0}
|
||||
{@const domain = domainR.data[0]}
|
||||
<Row>
|
||||
<Col md={8} lg={6}>
|
||||
<DomainInformationCard
|
||||
domainData={domain}
|
||||
{uid}
|
||||
{domainId}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col md={8} lg={6}>
|
||||
<ZoneHistoryCard {domainId} {uid} {zoneHistory} />
|
||||
</Col>
|
||||
</Row>
|
||||
{:else}
|
||||
<Alert color="warning">
|
||||
<h4 class="alert-heading">No data available</h4>
|
||||
<p>The domain response did not contain any data.</p>
|
||||
<hr />
|
||||
<Button type="button" color="secondary" outline href="/domains">
|
||||
<Icon name="arrow-left"></Icon>
|
||||
Back to Domains
|
||||
</Button>
|
||||
</Alert>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<Alert color="danger">
|
||||
<h4 class="alert-heading">Error loading domain</h4>
|
||||
<p>{error}</p>
|
||||
<hr />
|
||||
<Button type="button" color="secondary" outline href="/domains">
|
||||
<Icon name="arrow-left"></Icon>
|
||||
Back to Domains
|
||||
</Button>
|
||||
</Alert>
|
||||
{/await}
|
||||
</Container>
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2026 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 { goto } from '$app/navigation';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Form,
|
||||
FormGroup,
|
||||
Icon,
|
||||
Input,
|
||||
InputGroup,
|
||||
Label,
|
||||
Spinner,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { putUsersByUidDomainsByDomain } from '$lib/api-admin';
|
||||
import { toasts } from '$lib/stores/toasts';
|
||||
|
||||
interface Props {
|
||||
domainData: any;
|
||||
uid: string;
|
||||
domainId: string;
|
||||
}
|
||||
|
||||
let { domainData, uid, domainId }: Props = $props();
|
||||
|
||||
let domainName = $state('');
|
||||
let group = $state('');
|
||||
let id_owner = $state('');
|
||||
let id_provider = $state('');
|
||||
let loading = $state(false);
|
||||
let errorMessage = $state('');
|
||||
|
||||
// Update local state when domainData changes
|
||||
$effect(() => {
|
||||
if (domainData) {
|
||||
domainName = domainData.domain || '';
|
||||
group = domainData.group || '';
|
||||
id_owner = domainData.id_owner || '';
|
||||
id_provider = domainData.id_provider || '';
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
|
||||
loading = true;
|
||||
errorMessage = '';
|
||||
|
||||
try {
|
||||
await putUsersByUidDomainsByDomain({
|
||||
path: { uid, domain: domainId },
|
||||
body: {
|
||||
domain: domainName,
|
||||
group: group || undefined,
|
||||
id_owner: id_owner || undefined,
|
||||
id_provider: id_provider || undefined,
|
||||
}
|
||||
});
|
||||
|
||||
toasts.addToast({
|
||||
message: `Domain "${domainName}" has been updated successfully`,
|
||||
type: 'success',
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
goto('/domains');
|
||||
} catch (error) {
|
||||
errorMessage = 'Failed to update domain: ' + error;
|
||||
toasts.addErrorToast({
|
||||
message: errorMessage,
|
||||
timeout: 10000,
|
||||
});
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
goto('/domains');
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="mb-4">
|
||||
<CardHeader>
|
||||
<h5 class="mb-0">Domain Information</h5>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{#if errorMessage}
|
||||
<Alert color="danger" dismissible fade>
|
||||
{errorMessage}
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
<Form on:submit={handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for="domainId">Domain ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="domainId"
|
||||
value={domainData?.id || ''}
|
||||
disabled
|
||||
readonly
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="domainName">Domain Name (FQDN) *</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="domainName"
|
||||
bind:value={domainName}
|
||||
required
|
||||
placeholder="example.com"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
The fully qualified domain name.
|
||||
</small>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="group">Group</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="group"
|
||||
bind:value={group}
|
||||
placeholder="production"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
Optional hint string to group domains together.
|
||||
</small>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="id_owner">Owner ID</Label>
|
||||
<InputGroup>
|
||||
<Input
|
||||
type="text"
|
||||
id="id_owner"
|
||||
bind:value={id_owner}
|
||||
placeholder="owner-id"
|
||||
/>
|
||||
<Button
|
||||
color="secondary"
|
||||
outline
|
||||
href="/users/{id_owner}"
|
||||
disabled={!id_owner}
|
||||
>
|
||||
<Icon name="arrow-right"></Icon>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="id_provider">Provider ID</Label>
|
||||
<InputGroup>
|
||||
<Input
|
||||
type="text"
|
||||
id="id_provider"
|
||||
bind:value={id_provider}
|
||||
placeholder="provider-id"
|
||||
/>
|
||||
<Button
|
||||
color="secondary"
|
||||
outline
|
||||
href="/providers/{id_provider}"
|
||||
disabled={!id_provider}
|
||||
>
|
||||
<Icon name="arrow-right"></Icon>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</FormGroup>
|
||||
|
||||
<div class="d-flex gap-2 mt-4">
|
||||
<Button color="primary" type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<Spinner size="sm" class="me-2" />
|
||||
{:else}
|
||||
<Icon name="check-circle" class="me-2"></Icon>
|
||||
{/if}
|
||||
Save Changes
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="secondary"
|
||||
outline
|
||||
onclick={handleCancel}
|
||||
disabled={loading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
182
web-admin/src/routes/users/[uid]/domains/new/+page.svelte
Normal file
182
web-admin/src/routes/users/[uid]/domains/new/+page.svelte
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<!--
|
||||
This file is part of the happyDomain (R) project.
|
||||
Copyright (c) 2022-2026 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 { goto } from '$app/navigation';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Col,
|
||||
Container,
|
||||
Form,
|
||||
FormGroup,
|
||||
Icon,
|
||||
Input,
|
||||
Label,
|
||||
Row,
|
||||
Spinner,
|
||||
} from "@sveltestrap/sveltestrap";
|
||||
|
||||
import { postDomains } from '$lib/api-admin';
|
||||
import { toasts } from '$lib/stores/toasts';
|
||||
|
||||
let domain = $state('');
|
||||
let group = $state('');
|
||||
let id_owner = $state('');
|
||||
let id_provider = $state('');
|
||||
let loading = $state(false);
|
||||
let errorMessage = $state('');
|
||||
|
||||
async function handleSubmit() {
|
||||
loading = true;
|
||||
errorMessage = '';
|
||||
|
||||
try {
|
||||
const response = await postDomains({
|
||||
body: {
|
||||
domain: domain,
|
||||
group: group || undefined,
|
||||
id_owner: id_owner || undefined,
|
||||
id_provider: id_provider || undefined,
|
||||
}
|
||||
});
|
||||
|
||||
toasts.addToast({
|
||||
message: `Domain "${domain}" has been created successfully`,
|
||||
type: 'success',
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
goto('/domains');
|
||||
} catch (error) {
|
||||
errorMessage = 'Failed to create domain: ' + error;
|
||||
toasts.addErrorToast({
|
||||
message: errorMessage,
|
||||
timeout: 10000,
|
||||
});
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container class="flex-fill my-5">
|
||||
<Row class="mb-4">
|
||||
<Col>
|
||||
<h1 class="display-5">
|
||||
<Icon name="plus-circle"></Icon>
|
||||
Create New Domain
|
||||
</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col md={8} lg={6}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h5 class="mb-0">Domain Information</h5>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{#if errorMessage}
|
||||
<Alert color="danger" dismissible fade>
|
||||
{errorMessage}
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
<Form on:submit={handleSubmit}>
|
||||
<FormGroup>
|
||||
<Label for="domain">Domain Name (FQDN) *</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="domain"
|
||||
bind:value={domain}
|
||||
required
|
||||
placeholder="example.com"
|
||||
autofocus
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
The fully qualified domain name to manage.
|
||||
</small>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="group">Group</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="group"
|
||||
bind:value={group}
|
||||
placeholder="production"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
Optional hint string to group domains together.
|
||||
</small>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="id_owner">Owner ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="id_owner"
|
||||
bind:value={id_owner}
|
||||
placeholder="owner-id"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
The identifier of the domain's owner.
|
||||
</small>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<Label for="id_provider">Provider ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="id_provider"
|
||||
bind:value={id_provider}
|
||||
placeholder="provider-id"
|
||||
/>
|
||||
<small class="form-text text-muted">
|
||||
The identifier of the provider used to access and edit the domain.
|
||||
</small>
|
||||
</FormGroup>
|
||||
|
||||
<div class="d-flex gap-2 mt-4">
|
||||
<Button color="primary" type="submit" disabled={loading}>
|
||||
{#if loading}
|
||||
<Spinner size="sm" class="me-2" />
|
||||
{:else}
|
||||
<Icon name="plus-circle" class="me-2"></Icon>
|
||||
{/if}
|
||||
Create Domain
|
||||
</Button>
|
||||
<Button type="button" color="secondary" outline href="/domains" disabled={loading}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
Loading…
Add table
Add a link
Reference in a new issue