Continue to work on Svelte migration

This commit is contained in:
nemunaire 2022-12-07 16:07:14 +01:00
parent 1ac1f092b2
commit 1712d0c81d
19 changed files with 355 additions and 9 deletions

View File

@ -0,0 +1,18 @@
import { handleApiResponse } from '$lib/errors';
import type { Provider, ProviderList } from '$lib/model/provider';
export async function listProviders(): Promise<ProviderList> {
const res = await fetch('/api/providers/_specs', {
method: 'GET',
headers: {'Accept': 'application/json'},
});
return await handleApiResponse(res);
}
export async function getProviderSpec(psid: string): Promise<Provider> {
const res = await fetch(`/api/providers/_specs/` + psid, {
method: 'GET',
headers: {'Accept': 'application/json'},
});
return new Provider(await handleApiResponse(res));
}

View File

@ -2,7 +2,7 @@ import { handleApiResponse } from '$lib/errors';
import type { SignUpForm, LoginForm } from '$lib/model/user';
export async function registerUser(form: SignUpForm): Promise<any> {
const res = await fetch('api/users', {
const res = await fetch('/api/users', {
method: 'POST',
headers: {'Accept': 'application/json'},
body: JSON.stringify(form),
@ -11,7 +11,7 @@ export async function registerUser(form: SignUpForm): Promise<any> {
}
export async function authUser(form: LoginForm): Promise<any> {
const res = await fetch('api/auth', {
const res = await fetch('/api/auth', {
method: 'POST',
headers: {'Accept': 'application/json'},
body: JSON.stringify(form),
@ -20,7 +20,7 @@ export async function authUser(form: LoginForm): Promise<any> {
}
export async function logout(): Promise<any> {
const res = await fetch('api/auth/logout', {
const res = await fetch('/api/auth/logout', {
method: 'POST',
headers: {'Accept': 'application/json'},
});
@ -28,7 +28,7 @@ export async function logout(): Promise<any> {
}
export async function specialUserOperations(email: string, kind: "recovery"|"validation"): Promise<any> {
const res = await fetch('api/users', {
const res = await fetch('/api/users', {
method: 'PATCH',
headers: {'Accept': 'application/json'},
body: JSON.stringify({
@ -49,7 +49,7 @@ export async function resendValidationEmail(email: string): Promise<any> {
export async function recoverAccount(userid: string, key: string, password: string): Promise<any> {
userid = encodeURIComponent(userid);
const res = await fetch(`api/users/${userid}/recovery`, {
const res = await fetch(`/api/users/${userid}/recovery`, {
method: 'POST',
headers: {'Accept': 'application/json'},
body: JSON.stringify({
@ -62,7 +62,7 @@ export async function recoverAccount(userid: string, key: string, password: stri
export async function validateEmail(userid: string, key: string): Promise<any> {
userid = encodeURIComponent(userid);
const res = await fetch(`api/users/${userid}/email`, {
const res = await fetch(`/api/users/${userid}/email`, {
method: 'POST',
headers: {'Accept': 'application/json'},
body: JSON.stringify({

View File

@ -0,0 +1,79 @@
<script lang="ts">
import {
Badge,
CardHeader,
Col,
Container,
Row,
} from 'sveltestrap';
import Logo from '$lib/components/Logo.svelte';
import ZoneList from '$lib/components/ZoneList.svelte';
import { t } from '$lib/translations';
export let domains: Array<Domain>|undefined;
if (domains === undefined) {
}
export let filteredDomains = [];
export let filteredProvider = null;
function showDomain(dn) {
}
</script>
<Container class="pt-4 pb-5">
<h1 class="text-center mb-4">
{$t('common.welcome.start')}
<Logo height="40" />
{$t('common.welcome.end')}
</h1>
<Row>
<Col md="8" class="order-1 order-md-0">
<ZoneList
button
display-by-groups
domains={filteredDomains}
on:click={showDomain}
>
<Badge slot="badges" color="success">
OK
</Badge>
</ZoneList>
{#if filteredProvider}
<div class="card" class:mt-4={filteredDomains.length > 0}>
{#if !noDomainsList}
<CardHeader class="d-flex justify-content-between">
{$t("provider.provider")}
<em>{filteredProvider._comment}</em>
<Button
type="button"
color="secondary"
size="sm"
>
{$t('provider.import-domains')}
</Button>
</CardHeader>
{/if}
<h-provider-list-domains
ref="newDomains"
provider={filteredProvider}
show-domains-with-actions
on:no-domains-list-change={noDomainsList = $event}
/>
</div>
{/if}
{#if !filteredProvider || noDomainsList}
<h-list-group-input-new-domain
autofocus
class="mt-2"
my-provider={filteredProvider}
/>
{/if}
</Col>
</Row>
</Container>

View File

@ -0,0 +1,52 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import hList from '$lib/components/hList.svelte';
import { t } from '$lib/translations';
const dispatch = createEventDispatcher();
export let button = false;
export let display_by_groups = false;
export let domains = [];
</script>
<div>
{#if domains.length === 0}
<slot name="no-domain" />
{:else}
{#each Objects.keys(groups) as gname}
{@const gdomains = groups[gname]}
<div
class:border-top={Object.keys(groups).length != 1}
style="margin-top: 1.4em"
>
{#if Object.keys(groups).length != 1}
<div class="text-center" style="height: 1em">
<h3 class="d-inline-block px-1" style="background: white; position: relative; top: -.65em">
{#if group === undefined}
{$t("domaingroups.no-group")}
{:else}
{gname}
{/if}
</h3>
</div>
{/if}
<hList
items={gdomains}
{button}
on:click={(event) => dispatch('click', event.details)}
let:item={item}
>
<div class="text-monospace">
<div class="d-inline-block text-center" style="width: 50px;">
<ImgProvider id_provider={item.id_provider} />
</div>
{item.domain}
</div>
<slot name="badges" domain={item} />
</hList>
</div>
{/each}
{/if}
</div>

View File

@ -0,0 +1,12 @@
<script lang="ts">
export let id_provider;
</script>
{#if providers_getAll[id_provider]}
<img
src="'/api/providers/_specs/' + providers_getAll[id_provider]._srctype + '/icon.png'"
alt={providers_getAll[id_provider]._srctype}
title={providers_getAll[id_provider]._srctype}
style="max-width: 100%; max-height: 2.5em; margin: -.6em .4em -.6em -.6em"
>
{/if}

View File

@ -0,0 +1,50 @@
<script lang="ts">
import {
ListGroup,
ListGroupItem,
Spinner,
} from 'sveltestrap';
import ImgProvider from '$lib/components/providers/ImgProvider.svelte';
import { listProviders } from '$lib/api/provider_specs';
export let value = null;
let isLoading = false;
let providers = [];
listProviders().then((res) => providers = res)
</script>
<ListGroup>
{#if isLoading}
<ListGroupItem class="d-flex justify-content-center align-items-center">
<Spinner variant="primary" label="Spinning" class="mr-3" /> Retrieving usable providers...
</ListGroupItem>
{/if}
{#each Object.keys(providers) as ptype (ptype)}
{@const provider = providers[ptype]}
<ListGroupItem
active={value === provider.id}
button
class="d-flex"
on:click={() => value = provider.id}
>
<div
class="align-self-center text-center"
style="min-width:50px;width:50px;"
>
<ImgProvider
{provider}
style="max-width: 100%; max-height: 2.5em; margin: -.6em .4em -.6em -.6em"
/>
</div>
<div
class="align-self-center"
style="line-height: 1.1"
>
<strong>{provider.name}</strong> &ndash;
<small class="text-muted" title={provider.description}>{provider.description}</small>
</div>
</ListGroupItem>
{/each}
</ListGroup>

View File

@ -44,7 +44,10 @@
"run": "Run the request!",
"survey": "A remark? a comment to share? Don't hesitate to write us!",
"spinning": "Spinning",
"welcome": "Welcome to {{0}}!",
"welcome": {
"start": "Welcome to ",
"end": "!"
},
"help": "Help!",
"records": "{{number:eq; 0:no {{type}} record; 1:{{type}} record; default:{{type}} records}}"
},

View File

@ -43,7 +43,10 @@
"resolver": "Résolveur",
"run": "Lancer l'opération !",
"spinning": "Chargement",
"welcome": "Bienvenue sur {{0}} !",
"welcome": {
"start": "Bienvenue sur ",
"end": "!"
},
"help": "Besoin d'aide ?",
"records": "{{number:eq; 0:aucun enregistrement {{type}}; 1:enregistrement {{type}}; default:enregistrements {{type}}}}",
"survey": "Une remarque ? un commentaire à partager ? N'hésitez pas à nous écrire !"

View File

@ -0,0 +1,8 @@
export interface Domain {
id: string;
id_owner: string;
id_provider: string;
domain: string;
group: string;
zone_history: Array<string>;
};

View File

@ -0,0 +1,3 @@
export interface Map<T1, T2> {
[key: T1]: T2;
};

View File

@ -0,0 +1,26 @@
import type Map from '$lib/model/golang';
export class ProviderInfos {
name: string;
description: string;
capabilites: Array<string>;
constructor({ name, description, capabilites }: ProviderInfos) {
this.name = name;
this.description = description;
this.capabilites = capabilites;
}
};
export type ProviderList = Map<string, ProviderInfos>;
export interface ProviderMeta {
_srctype: string;
_id: string;
_ownerid: string;
_comment: string;
};
export interface Provider extends ProviderMeta {
};

View File

@ -0,0 +1,11 @@
export interface ServiceMeta {
_svctype: string;
_id: string;
_ownerid: string;
_domain: string;
_ttl: number;
_comment: string;
_mycomment: string;
_aliases: Array<string>;
_tmp_hint_nb: number;
};

View File

@ -0,0 +1,6 @@
export interface SourceMeta {
_srctype: string;
_id: string;
_ownerid: string;
_comment: string;
};

9
ui/src/lib/model/zone.ts Normal file
View File

@ -0,0 +1,9 @@
export interface ZoneMeta {
id: string;
id_author: string;
default_ttl: Number;
last_modified: Date;
commit_message?: string;
commit_date?: Date;
published?: Date;
};

View File

@ -0,0 +1,16 @@
import { writable, type Writable } from 'svelte/store';
import type { User } from '$lib/model/user';
export const userSession: Writable<null | User> = writable(null);
export async function refreshUserSession() {
const res = await fetch('/api/auth', {headers: {'Accept': 'application/json'}})
if (res.status == 200) {
const user = new User(await res.json());
userSession.set(user);
return user
} else {
userSession.set(null);
throw new Error((await res.json()).errmsg);
}
}

View File

@ -0,0 +1,5 @@
<script>
import Home from '$lib/components/Home.svelte';
</script>
<Home />

View File

@ -5,7 +5,9 @@ import { get_store_value } from 'svelte/internal';
import { userSession } from '$lib/stores/usersession';
import { config as tsConfig, locale } from '$lib/translations';
export const load: Load = async() => {
export const load: Load = async({ parent }) => {
await parent();
// If not connected, redirect to main website in the right language
if (!get_store_value(userSession)) {
const initLocale = locale.get() || window.navigator.language || window.navigator.languages[0] || tsConfig.fallbackLocale;

View File

@ -0,0 +1,25 @@
<script lang="ts">
import {
Button,
Container,
Icon,
} from 'sveltestrap';
import { t } from '$lib/translations';
</script>
<Container class="d-flex flex-column mt-4" fluid>
<h1 class="text-center mb-3">
<Button
type="button"
class="fw-bolder"
color="link"
on:click={() => history.go(-1)}
>
<Icon name="chevron-left" />
</Button>
{$t("provider.select-provider")}
</h1>
<hr class="mt-0 mb-0">
<slot class="flex-grow-1"></slot>
</Container>

View File

@ -0,0 +1,18 @@
<script lang="ts">
import {
Col,
Row,
} from 'sveltestrap';
import ProviderSelector from '$lib/components/providers/Selector.svelte';
function selectNewProvider() {
}
</script>
<Row class="my-3">
<Col offset-md="2" md="8">
<ProviderSelector on:provider-selected={selectNewProvider} />
</Col>
</Row>