Skip to content

Commit

Permalink
more actions
Browse files Browse the repository at this point in the history
  • Loading branch information
LucaRickli committed Jan 11, 2025
1 parent a9373b8 commit 50e87ed
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 71 deletions.
2 changes: 0 additions & 2 deletions src/lib/api/headscale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -716,8 +716,6 @@ export class Acl {
const p: V1Policy = policy?.length ? parse(stripJsonTrailingCommas(policy)) : undefined;
const comments = p as unknown as V1PolicyComments;

console.debug({ p, comments, policy });

return {
policy: p,
comments,
Expand Down
18 changes: 13 additions & 5 deletions src/lib/components/data/acl/AccessControlInfo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
import type { Acl, User } from '$lib/api';
import RuleInfo from './RuleInfo.svelte';
import Plus from 'lucide-svelte/icons/plus';
import CreateRule from './CreateRule.svelte';
import { createEventDispatcher } from 'svelte';
export let acl: Acl;
export let users: User[] | undefined;
const dispatch = createEventDispatcher<{ close: undefined }>();
</script>

<Sheet.Root>
Expand All @@ -21,16 +25,20 @@

<ul class="menu">
<li>
<button disabled>
<Plus />
<span> Rule </span>
</button>
<CreateRule {acl} {users} on:submit={() => dispatch('close')}>
<svelte:fragment slot="trigger" let:builder>
<button {...builder} use:builder.action>
<Plus />
<span> Rule </span>
</button>
</svelte:fragment>
</CreateRule>
</li>
</ul>

<div class="!mt-8 space-y-4">
{#each acl?.acls || [] as rule}
<RuleInfo {acl} {users} {rule} />
<RuleInfo {acl} {users} {rule} on:close />
{/each}
</div>

Expand Down
139 changes: 139 additions & 0 deletions src/lib/components/data/acl/CreateRule.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<script lang="ts">
import { defaults, superForm } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { createEventDispatcher } from 'svelte';
import { writable } from 'svelte/store';
import { z } from 'zod';
import * as Select from '$lib/components/ui/select/index.js';
import * as Sheet from '$lib/components/ui/sheet';
import { Textarea } from '$lib/components/ui/textarea';
import * as Form from '$lib/components/form';
import type { Acl, AclData, User } from '$lib/api';
import SelectRuleSource from './SelectRuleSource.svelte';
import SelectRuleTarget from './SelectRuleTarget.svelte';
import { errorToast, successToast } from '$lib/utils/toast';
import { formatError } from '$lib/utils/error';
export let acl: Acl;
export let users: User[] | undefined;
const dispatch = createEventDispatcher<{ submit: undefined }>();
let mainSheet: InstanceType<typeof Sheet.Root>;
const schema: z.ZodType<Omit<AclData['acls'][0], 'id'>> = z.object({
action: z.literal('accept'),
src: z.array(z.string()).min(1),
dst: z.array(z.object({ host: z.string(), port: z.string() })).min(1),
comments: z.array(z.string()).default([])
});
const formDefaults = defaults(zod(schema));
const form = superForm(
{ ...formDefaults, data: { ...formDefaults.data, action: 'accept' } },
{
SPA: true,
dataType: 'json',
invalidateAll: true,
validators: zod(schema),
async onUpdate({ form }) {
if (form.valid) {
try {
acl.acls.push({
...form.data,
id: '0'
});
const { error } = await acl.save();
if (error) throw error;
successToast('Created new rule');
dispatch('submit');
mainSheet.close();
} catch (err) {
console.error(err);
errorToast(formatError(err));
}
}
}
}
);
const { form: formData } = form;
const description = writable<string>('');
description.subscribe((desc) => {
formData.update((data) => ({ ...data, comments: desc.split(/\n{1,}/gm).map((i) => i.trim()) }));
});
</script>

<Sheet.Root bind:this={mainSheet}>
<Sheet.Trigger asChild let:builder>
<slot name="trigger" {builder} />
</Sheet.Trigger>

<Sheet.Content side="left">
<Sheet.Header>
<Sheet.Title>Create rule</Sheet.Title>
</Sheet.Header>

<Form.Root {form} submitText="Create">
<Form.Field {form} name="action" let:constraints>
<Form.Control let:attrs>
<Form.Label>Action</Form.Label>
<Select.Root {...attrs} {...constraints} selected={{ label: 'accept', value: 'accept' }}>
<Select.Trigger>
<Select.Value />
</Select.Trigger>

<Select.Content class="!mt-0">
<Select.Group>
<Select.Item value="accept">accept</Select.Item>
</Select.Group>
</Select.Content>
</Select.Root>
</Form.Control>
</Form.Field>

<Form.Field {form} name="src" let:constraints>
<Form.Control let:attrs>
<Form.Label class="required" for={attrs.id}>Source</Form.Label>
<SelectRuleSource
{...attrs}
{...constraints}
{acl}
{users}
bind:selected={$formData.src}
/>
<Form.FieldErrors />
</Form.Control>
</Form.Field>

<Form.Field {form} name="dst" let:constraints>
<Form.Control let:attrs>
<Form.Label class="required" for={attrs.id}>Destination</Form.Label>
<SelectRuleTarget
{...attrs}
{...constraints}
{acl}
{users}
bind:selected={$formData.dst}
/>
<Form.FieldErrors />
</Form.Control>
</Form.Field>

<Form.Field {form} name="comments" let:constraints>
<Form.Control let:attrs>
<Form.Label for={attrs.id}>Description</Form.Label>
<Textarea {...attrs} {...constraints} bind:value={$description} />
</Form.Control>
</Form.Field>
</Form.Root>
</Sheet.Content>
</Sheet.Root>
98 changes: 59 additions & 39 deletions src/lib/components/data/acl/EditRule.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,64 @@
import SelectRuleSource from './SelectRuleSource.svelte';
import SelectRuleTarget from './SelectRuleTarget.svelte';
import { errorToast, successToast } from '$lib/utils/toast';
import { formatError } from '$lib/utils/error';
export let acl: Acl;
export let users: User[] | undefined;
export let rule: AclData['acls'][0];
const dispatch = createEventDispatcher<{ submit: undefined }>();
let mainSheet: InstanceType<typeof Sheet.Root>;
const schema: z.ZodType<Omit<AclData['acls'][0], 'id'>> = z.object({
action: z.literal('accept'),
src: z.array(z.string()),
dst: z.array(z.object({ host: z.string(), port: z.string() })),
comments: z.array(z.string()).default([])
});
const form = superForm(defaults(zod(schema)), {
SPA: true,
dataType: 'json',
invalidateAll: true,
validators: zod(schema),
async onUpdate({ form }) {
if (form.valid) {
console.debug('form is valid');
dispatch('submit');
const formDefaults = defaults(zod(schema));
const form = superForm(
{ ...formDefaults, data: rule },
{
SPA: true,
dataType: 'json',
invalidateAll: true,
validators: zod(schema),
async onUpdate({ form }) {
if (form.valid) {
try {
acl.acls = acl.acls.map((acl) =>
acl.id === rule.id ? { ...rule, ...form.data } : rule
);
const { error } = await acl.save();
if (error) throw error;
successToast(`Saved rule ${rule.id}`);
dispatch('submit');
mainSheet.close();
} catch (err) {
console.error(err);
errorToast(formatError(err));
}
}
}
}
});
);
const { form: formData, constraints } = form;
const { form: formData } = form;
const description = writable<string>(rule.comments?.join('\n') || '');
const description = writable<string>(rule.comments?.join('\n\n') || '');
description.subscribe((desc) => {
formData.update((data) => ({ ...data, comments: desc.split(/\n{1,}/gm) }));
});
export function reset() {
formData.set({
action: 'accept',
src: rule.src,
dst: rule.dst,
comments: get(description).split(/\n{2,}/gm)
});
}
reset();
</script>

<Sheet.Root>
<Sheet.Root bind:this={mainSheet}>
<Sheet.Trigger asChild let:builder>
<slot name="trigger" {builder} />
</Sheet.Trigger>
Expand All @@ -72,12 +82,11 @@
<Sheet.Title>Edit rule</Sheet.Title>
</Sheet.Header>

<Form.Root {form} {reset} submitText="Save">
<Form.Field {form} name="action">
<Form.Root {form} submitText="Save">
<Form.Field {form} name="action" let:constraints>
<Form.Control let:attrs>
<Form.Label>Action</Form.Label>

<Select.Root selected={{ label: 'accept', value: 'accept' }}>
<Select.Root {...attrs} {...constraints} selected={{ label: 'accept', value: 'accept' }}>
<Select.Trigger>
<Select.Value />
</Select.Trigger>
Expand All @@ -91,27 +100,38 @@
</Form.Control>
</Form.Field>

<Form.Field {form} name="src">
<Form.Field {form} name="src" let:constraints>
<Form.Control let:attrs>
<Form.Label for={attrs.id}>Source</Form.Label>

<SelectRuleSource {...attrs} {acl} {users} bind:selected={$formData.src} />
<Form.Label class="required" for={attrs.id}>Source</Form.Label>
<SelectRuleSource
{...attrs}
{...constraints}
{acl}
{users}
bind:selected={$formData.src}
/>
<Form.FieldErrors />
</Form.Control>
</Form.Field>

<Form.Field {form} name="dst">
<Form.Field {form} name="dst" let:constraints>
<Form.Control let:attrs>
<Form.Label for={attrs.id}>Destination</Form.Label>

<SelectRuleTarget {...attrs} {acl} {users} bind:selected={$formData.dst} />
<Form.Label class="required" for={attrs.id}>Destination</Form.Label>
<SelectRuleTarget
{...attrs}
{...constraints}
{acl}
{users}
bind:selected={$formData.dst}
/>
<Form.FieldErrors />
</Form.Control>
</Form.Field>

<Form.Field {form} name="comments">
<Form.Field {form} name="comments" let:constraints>
<Form.Control let:attrs>
<Form.Label for={attrs.id}>Description</Form.Label>

<Textarea {...attrs} {...$constraints.comments} bind:value={$description} />
<Textarea {...attrs} {...constraints} bind:value={$description} />
</Form.Control>
</Form.Field>
</Form.Root>
Expand Down
5 changes: 4 additions & 1 deletion src/lib/components/data/acl/RuleInfo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@
import { Label } from '$lib/components/ui/label';
import EditRule from './EditRule.svelte';
import { createEventDispatcher } from 'svelte';
export let acl: Acl;
export let users: User[] | undefined;
export let rule: AclData['acls'][0];
export let prefixes: { in: Set<string>; out: Set<string> } | undefined = undefined;
const dispatch = createEventDispatcher<{ close: undefined }>();
</script>

<div class="space-y-3 border-b pb-5 last:border-b-0 [&>div]:space-y-2">
<div>
<div class="flex items-center justify-between gap-2">
<Label>Source</Label>
<div class="flex items-center gap-1.5">
<EditRule {rule} {acl} {users}>
<EditRule {rule} {acl} {users} on:submit={() => dispatch('close')}>
<svelte:fragment slot="trigger" let:builder>
<button
{...builder}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/data/apikey/ApikeyInfo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
export let apikeys: ApiKey[] | undefined = undefined;
const dispatch = createEventDispatcher<{ submit: undefined }>();
const dispatch = createEventDispatcher<{ close: undefined }>();
if (!apikeys) {
ApiKey.list().then(({ data }) => {
Expand All @@ -34,7 +34,7 @@
if (error) throw error;
successToast(`Expired API key "${key.prefix}..."`);
dispatch('submit');
dispatch('close');
} catch (err) {
console.error(err);
errorToast(formatError(err));
Expand Down
Loading

0 comments on commit 50e87ed

Please sign in to comment.