Compare commits

..

No commits in common. "3cd07d7e633aedd4943a6076f3d85b3ebfbef54d" and "ab8430824f94fbe11fa78312326fe882476b6c70" have entirely different histories.

13 changed files with 118 additions and 164 deletions

View file

@ -30,10 +30,6 @@
}
},
"tab_size": 2
},
"CSS": {
"format_on_save": "off",
"tab_size": 2
}
}
}

View file

@ -1,34 +0,0 @@
<script setup lang="ts">
import { variantOf, atPath } from '~/util';
import type { Component } from 'vue';
const props = withDefaults(defineProps<{
data: object | string,
definition: { [key: string]: { path: string, component: Component | undefined } } | undefined,
}>(), {
data: '',
definition: undefined,
});
const value = computed(() => {
let variant = variantOf(props.data);
if (variant == null) {
return {};
}
if (props.definition === undefined) {
return {};
}
let thing = props.definition[variant];
if (thing == null) {
return {};
}
return { data: atPath(props.data, thing.path), component: thing.component };
});
</script>
<template>
<component :is="value.component" v-if="value.component" :data="value.data"/>
<div v-else>
{{ value.data }}
</div>
</template>

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { useKeyModifier } from '@vueuse/core';
import type { Component } from 'vue';
import { equals, atPath } from '~/util';
import { equals } from '~/util';
const shiftState = $(useKeyModifier('Shift'));
const ctrlState = $(useKeyModifier('Control'));
@ -19,7 +19,6 @@ const props = withDefaults(defineProps<{
heading: string,
path: string,
component: Component,
props: any,
}[],
sortSelf?: boolean,
draggable?: boolean,
@ -95,6 +94,12 @@ function toggleRowSelection(index: number) {
emit('selectionChanged');
}
function atPath(value: any, path: string): any {
for (const segment of path.split('.'))
value = (value ?? {} as any)[segment];
return value;
}
let draggedRow = $ref(-1);
let draggedOverRow = $ref(-1);
function dragDropRow() {
@ -144,9 +149,9 @@ function dragDropRow() {
@dragstart="() => draggedRow = index"
@dragenter="() => draggedOverRow = index"
@dragend="() => dragDropRow()">
<td v-for="col of columns" :key="col.path">
<component :is="col.component" v-if="col.component" :data="atPath(row, col.path)" v-bind="col.props"/>
<template v-else>{{ atPath(row, col.path) }}</template>
<td v-for="{path, component} of columns" :key="path">
<component :is="component" v-if="component" :data="atPath(row, path)"/>
<template v-else>{{ atPath(row, path) }}</template>
</td>
</tr>
</tbody>

View file

@ -1,33 +0,0 @@
<script setup lang="ts">
const props = withDefaults(defineProps<{
data: match | undefined,
}>(), {
data: undefined,
});
type port = { single?: { port: number }, range?: { start_port: number, end_port: number } };
type portOrAny = string | port;
type match = { source: portOrAny, destination: portOrAny };
const value = computed(() => {
if (props.data === undefined) {
return 'unknown';
}
return `${computePort(props.data.source)}->${computePort(props.data.destination)}`;
});
function computePort(port: portOrAny) {
if (typeof port === 'string') {
return 'any';
} else if (port.single !== undefined) {
return port.single.port;
} else if (port.range != undefined){
return `(${port.range.start_port}-${port.range.end_port})`;
} else {
return 'unknown';
}
}
</script>
<template>
{{ value }}
</template>

View file

@ -1,7 +1,21 @@
<!-- Base component that implements selecting single and multiple values from a list in a type-unsafe manner -->
<script lang="ts">
// Types
export type Options = Record<Index, Option>;
export type Option = {
[key: Index]: any, // Allow additional properties for customization
display?: string,
};
export type SearchProvider = (opts: SearchOptions) => Promise<Options>;
export type MaybeSearchProvider = SearchProvider | null;
export type SearchOptions = {
search: string,
unknownKeys?: Index[],
// parentData?: any,
};
</script>
<script setup lang="ts">
import { equals, isNullish, Index } from '../../util';
import type { Options, MaybeSearchProvider } from './input';
// --- Prop setup ---
const props = withDefaults(defineProps<{
// Two-Way Bindings (v-model)
@ -75,9 +89,9 @@ watch($$(multiple), () => modelValue = multiple ? [] : null );
// --- Everything Else ---
let expanded = $ref(false);
let navigated = $ref(0);
let inputDiv = $ref(null as HTMLElement | null);
let input = $ref(null as HTMLElement | null);
let valueButton = $ref(null as HTMLElement | null);
let inputDiv: HTMLElement | null = $ref(null);
let input: HTMLElement | null = $ref(null);
let valueButton: HTMLElement | null = $ref(null);
const selCount = $computed(() => modelValue?.length || 0);
@ -252,4 +266,4 @@ input { padding: 0.25rem; outline: none; }
div:empty { display: none; }
button { padding: 0.25rem; }
</style>
</style>

View file

@ -1,6 +1,19 @@
<script lang="ts">
import { Index, MaybeIndex, equals, variantOf } from '../../util';
import { Fields } from './NicerForm.vue';
export type Variant = {
fields?: Fields,
display?: string,
icon?: Component
};
export type Variants = Record<Index, Variant>;
export type EnumValueWithFields = { [index: Index]: Record<Index, any> }
export type EnumValue = Index | EnumValueWithFields;
export type MaybeEnumValue = EnumValue | null;
</script>
<script setup lang="ts">
import type { MaybeEnumValue, Variants, EnumValueWithFields } from './input';
import { equals, variantOf, MaybeIndex, Index } from '~/util';
const props = withDefaults(defineProps<{
// Two-Way Bindings

View file

@ -1,6 +1,17 @@
<script setup lang="ts">
<script lang="ts">
import { equals, Index } from '../../util';
import type { Fields } from './input';
export type Field = {
is: Component | string,
label?: string,
props?: any,
// actions?: Action[],
// forceCastNumber`?: bool,
};
export type Fields = Record<Index, Field>;
</script>
<script setup lang="ts">
const props = withDefaults(defineProps<{
// Two-Way Bindings
modelValue?: Record<Index, any>,

View file

@ -1,35 +0,0 @@
import { Index } from '~/util';
// --- DROPDOWN_INPUT ---
export type Options = Record<Index, Option>;
export type Option = {
[key: Index]: any, // Allow additional properties for customization
display?: string,
};
export type SearchProvider = (opts: SearchOptions) => Promise<Options>;
export type MaybeSearchProvider = SearchProvider | null;
export type SearchOptions = {
search: string,
unknownKeys?: Index[],
// parentData?: any,
};
// --- FORM INPUT ---
export type Field = {
is: Component | string,
label?: string,
props?: any,
// actions?: Action[],
// forceCastNumber`?: bool,
};
export type Fields = Record<Index, Field>;
// --- ENUM INPUT ---
export type Variant = {
fields?: Fields,
display?: string,
icon?: Component
};
export type Variants = Record<Index, Variant>;
export type EnumValueWithFields = { [index: Index]: Record<Index, any> }
export type EnumValue = Index | EnumValueWithFields;
export type MaybeEnumValue = EnumValue | null;

View file

@ -1,4 +1,4 @@
import { SearchProvider, Options } from '~/components/input/input';
import { SearchProvider, Options } from '~/components/input/DropdownInput.vue';
import { apiCall } from './api';
const GetHardwareInterfaces: SearchProvider = async (o) => {
@ -16,7 +16,7 @@ const GetInterfaces: SearchProvider = async (o) => {
let res = await apiCall('network.interfaces.list', {});
if (res.Error === null) {
console.debug('interfaces', res.Data);
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
} else {
console.debug('error', res);
return {} as Options;
@ -27,7 +27,7 @@ const GetAddresses: SearchProvider = async (o) => {
let res = await apiCall('object.addresses.list', {});
if (res.Error === null) {
console.debug('addresses', res.Data);
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
} else {
console.debug('error', res);
return {} as Options;
@ -38,7 +38,7 @@ const GetServices: SearchProvider = async (o) => {
let res = await apiCall('object.services.list', {});
if (res.Error === null) {
console.debug('services', res.Data);
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
} else {
console.debug('error', res);
return {} as Options;
@ -49,7 +49,7 @@ const GetPeers: SearchProvider = async (o) => {
let res = await apiCall('vpn.wireguard.peers.list', {});
if (res.Error === null) {
console.debug('peers', res.Data);
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
} else {
console.debug('error', res);
return {} as Options;
@ -249,7 +249,7 @@ export const editTypes: { [key: string]: { [key: string]: any } } = {
'icmp': {
display: 'ICMP',
fields: {
code: { is: 'NumberBox', label: 'ICMP Code' },
icmp_code: { is: 'NumberBox', label: 'ICMP Code' },
},
},
'group': {
@ -362,4 +362,4 @@ export const editTypes: { [key: string]: { [key: string]: any } } = {
},
},
},
};
};

View file

@ -68,11 +68,7 @@ th, td {
padding: 0.5rem;
border: 0.125rem solid var(--cl-bg-el);
}
td {
vertical-align: top;
}
th>* {
th > *{
justify-content: center;
align-items: center;
}

View file

@ -1,27 +1,17 @@
<script setup lang="ts">
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import EnumValueDisplay from '~/components/display/EnumValueDisplay.vue';
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
const p = getPlugins();
let addresses = $ref([]);
let loading = $ref(false);
let selection = $ref([] as number[]);
const addressValueDefinition: { [key: string]: { path: string, component: Component | undefined } } = {
'host': { path: 'host.address', component: undefined },
'range': { path: 'range.address', component: undefined },
'network': { path: 'network.address', component: undefined },
'group': { path: 'group.members', component: ArrayDisplay },
};
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Type', path: 'address_type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Value', path: 'address_type', component: markRaw(EnumValueDisplay), componentProp: 'data', props: { definition: addressValueDefinition } },
{ heading: 'Type', path: 'type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Value', path: 'value' },
{ heading: 'Comment', path: 'comment' },
];
@ -43,13 +33,35 @@ const displayData = $computed(() => {
for (const index in addresses) {
data.push({
name: addresses[index].name,
address_type: addresses[index].address_type,
value: getAddressValue(addresses[index]),
type: addresses[index].address_type,
comment: addresses[index].comment,
});
}
return data;
});
function getAddressValue(s: any): string {
let value: string;
switch (s.address_type) {
case 'host':
value = s.host;
break;
case 'range':
value = s.range;
break;
case 'network':
value = s.network;
break;
case 'group':
value = s.children;
break;
default:
value = 'unkown';
}
return value;
}
async function deleteAddress(){
let res = await apiCall('object.addresses_delete', { id: displayData[selection[0]].name });
if (res.Error === null) {

View file

@ -2,26 +2,16 @@
import { apiCall } from '../../api';
import getPlugins from '../../plugins';
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
import EnumValueDisplay from '~/components/display/EnumValueDisplay.vue';
import PortServiceDisplay from '~/components/display/PortServiceDisplay.vue';
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
const p = getPlugins();
let services = $ref({});
let loading = $ref(false);
let selection = $ref([] as number[]);
const serviceValueDefinition: { [key: string]: { path: string, component: Component | undefined } } = {
'tcp': { path: 'tcp', component: PortServiceDisplay },
'udp': { path: 'udp', component: PortServiceDisplay },
'icmp': { path: 'icmp.code', component: undefined },
'group': { path: 'group.members', component: ArrayDisplay },
};
const columns = [
{ heading: 'Name', path: 'name' },
{ heading: 'Type', path: 'service_type', component: markRaw(EnumTypeDisplay), componentProp: 'data', props: { definition: serviceValueDefinition } },
{ heading: 'Value', path: 'service_type', component: markRaw(EnumValueDisplay), componentProp: 'data', props: { definition: serviceValueDefinition } },
{ heading: 'Type', path: 'type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
{ heading: 'Value', path: 'value' },
{ heading: 'Comment', path: 'comment' },
];
@ -31,13 +21,38 @@ const displayData = $computed(() => {
for (const index in services) {
data.push({
name: services[index].name,
service_type: services[index].service_type,
value: getServiceValue(services[index]),
type: Object.keys(services[index].service_type)[0],
comment: services[index].comment,
});
}
return data;
});
function getServiceValue(s: any): string {
console.debug('test', Object.keys(s.service_type)[0]);
let value: string;
switch (Object.keys(s.service_type)[0]) {
case 'tcp':
case 'udp':
value = getServicePortRange(s.service_type[Object.keys(s.service_type)[0]]);
break;
case 'icmp':
value = 'icmp';
break;
case 'group':
value = s.children;
break;
default:
value = 'unkown';
}
return value;
}
function getServicePortRange(s:any): string {
return `${s.source}-${s.destination}`;
}
async function load(){
loading = true;
let res = await apiCall('object.services.list', {});

View file

@ -23,9 +23,3 @@ export function variantOf(enumValue: any) {
export type Index = string | number | symbol;
export type MaybeIndex = Index | null;
export function atPath(value: any, path: string): any {
for (const segment of path.split('.'))
value = (value ?? {} as any)[segment];
return value;
}