mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-12 19:28:20 +00:00
Compare commits
11 commits
ab8430824f
...
3cd07d7e63
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3cd07d7e63 | ||
![]() |
822209cd18 | ||
![]() |
e1ee456c3a | ||
f34fb0e4bd | |||
75432f62b8 | |||
05b6a2c5d1 | |||
3c49416732 | |||
c168fb1a60 | |||
2ae4c7f5fb | |||
6019025df2 | |||
f736e53f14 |
13 changed files with 164 additions and 118 deletions
|
@ -30,6 +30,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tab_size": 2
|
"tab_size": 2
|
||||||
|
},
|
||||||
|
"CSS": {
|
||||||
|
"format_on_save": "off",
|
||||||
|
"tab_size": 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
client/src/components/display/EnumValueDisplay.vue
Normal file
34
client/src/components/display/EnumValueDisplay.vue
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<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>
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useKeyModifier } from '@vueuse/core';
|
import { useKeyModifier } from '@vueuse/core';
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import { equals } from '~/util';
|
import { equals, atPath } from '~/util';
|
||||||
|
|
||||||
const shiftState = $(useKeyModifier('Shift'));
|
const shiftState = $(useKeyModifier('Shift'));
|
||||||
const ctrlState = $(useKeyModifier('Control'));
|
const ctrlState = $(useKeyModifier('Control'));
|
||||||
|
@ -19,6 +19,7 @@ const props = withDefaults(defineProps<{
|
||||||
heading: string,
|
heading: string,
|
||||||
path: string,
|
path: string,
|
||||||
component: Component,
|
component: Component,
|
||||||
|
props: any,
|
||||||
}[],
|
}[],
|
||||||
sortSelf?: boolean,
|
sortSelf?: boolean,
|
||||||
draggable?: boolean,
|
draggable?: boolean,
|
||||||
|
@ -94,12 +95,6 @@ function toggleRowSelection(index: number) {
|
||||||
emit('selectionChanged');
|
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 draggedRow = $ref(-1);
|
||||||
let draggedOverRow = $ref(-1);
|
let draggedOverRow = $ref(-1);
|
||||||
function dragDropRow() {
|
function dragDropRow() {
|
||||||
|
@ -149,9 +144,9 @@ function dragDropRow() {
|
||||||
@dragstart="() => draggedRow = index"
|
@dragstart="() => draggedRow = index"
|
||||||
@dragenter="() => draggedOverRow = index"
|
@dragenter="() => draggedOverRow = index"
|
||||||
@dragend="() => dragDropRow()">
|
@dragend="() => dragDropRow()">
|
||||||
<td v-for="{path, component} of columns" :key="path">
|
<td v-for="col of columns" :key="col.path">
|
||||||
<component :is="component" v-if="component" :data="atPath(row, path)"/>
|
<component :is="col.component" v-if="col.component" :data="atPath(row, col.path)" v-bind="col.props"/>
|
||||||
<template v-else>{{ atPath(row, path) }}</template>
|
<template v-else>{{ atPath(row, col.path) }}</template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
33
client/src/components/display/PortServiceDisplay.vue
Normal file
33
client/src/components/display/PortServiceDisplay.vue
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<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>
|
|
@ -1,21 +1,7 @@
|
||||||
<!-- Base component that implements selecting single and multiple values from a list in a type-unsafe manner -->
|
<!-- 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">
|
<script setup lang="ts">
|
||||||
import { equals, isNullish, Index } from '../../util';
|
import { equals, isNullish, Index } from '../../util';
|
||||||
|
import type { Options, MaybeSearchProvider } from './input';
|
||||||
// --- Prop setup ---
|
// --- Prop setup ---
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
// Two-Way Bindings (v-model)
|
// Two-Way Bindings (v-model)
|
||||||
|
@ -89,9 +75,9 @@ watch($$(multiple), () => modelValue = multiple ? [] : null );
|
||||||
// --- Everything Else ---
|
// --- Everything Else ---
|
||||||
let expanded = $ref(false);
|
let expanded = $ref(false);
|
||||||
let navigated = $ref(0);
|
let navigated = $ref(0);
|
||||||
let inputDiv: HTMLElement | null = $ref(null);
|
let inputDiv = $ref(null as HTMLElement | null);
|
||||||
let input: HTMLElement | null = $ref(null);
|
let input = $ref(null as HTMLElement | null);
|
||||||
let valueButton: HTMLElement | null = $ref(null);
|
let valueButton = $ref(null as HTMLElement | null);
|
||||||
|
|
||||||
const selCount = $computed(() => modelValue?.length || 0);
|
const selCount = $computed(() => modelValue?.length || 0);
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
<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">
|
<script setup lang="ts">
|
||||||
|
import type { MaybeEnumValue, Variants, EnumValueWithFields } from './input';
|
||||||
|
import { equals, variantOf, MaybeIndex, Index } from '~/util';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
// Two-Way Bindings
|
// Two-Way Bindings
|
||||||
|
|
|
@ -1,17 +1,6 @@
|
||||||
<script lang="ts">
|
|
||||||
import { equals, Index } from '../../util';
|
|
||||||
|
|
||||||
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">
|
<script setup lang="ts">
|
||||||
|
import { equals, Index } from '../../util';
|
||||||
|
import type { Fields } from './input';
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
// Two-Way Bindings
|
// Two-Way Bindings
|
||||||
modelValue?: Record<Index, any>,
|
modelValue?: Record<Index, any>,
|
||||||
|
|
35
client/src/components/input/input.ts
Normal file
35
client/src/components/input/input.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
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;
|
|
@ -1,4 +1,4 @@
|
||||||
import { SearchProvider, Options } from '~/components/input/DropdownInput.vue';
|
import { SearchProvider, Options } from '~/components/input/input';
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
|
|
||||||
const GetHardwareInterfaces: SearchProvider = async (o) => {
|
const GetHardwareInterfaces: SearchProvider = async (o) => {
|
||||||
|
@ -16,7 +16,7 @@ const GetInterfaces: SearchProvider = async (o) => {
|
||||||
let res = await apiCall('network.interfaces.list', {});
|
let res = await apiCall('network.interfaces.list', {});
|
||||||
if (res.Error === null) {
|
if (res.Error === null) {
|
||||||
console.debug('interfaces', res.Data);
|
console.debug('interfaces', res.Data);
|
||||||
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
|
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
|
||||||
} else {
|
} else {
|
||||||
console.debug('error', res);
|
console.debug('error', res);
|
||||||
return {} as Options;
|
return {} as Options;
|
||||||
|
@ -27,7 +27,7 @@ const GetAddresses: SearchProvider = async (o) => {
|
||||||
let res = await apiCall('object.addresses.list', {});
|
let res = await apiCall('object.addresses.list', {});
|
||||||
if (res.Error === null) {
|
if (res.Error === null) {
|
||||||
console.debug('addresses', res.Data);
|
console.debug('addresses', res.Data);
|
||||||
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
|
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
|
||||||
} else {
|
} else {
|
||||||
console.debug('error', res);
|
console.debug('error', res);
|
||||||
return {} as Options;
|
return {} as Options;
|
||||||
|
@ -38,7 +38,7 @@ const GetServices: SearchProvider = async (o) => {
|
||||||
let res = await apiCall('object.services.list', {});
|
let res = await apiCall('object.services.list', {});
|
||||||
if (res.Error === null) {
|
if (res.Error === null) {
|
||||||
console.debug('services', res.Data);
|
console.debug('services', res.Data);
|
||||||
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
|
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
|
||||||
} else {
|
} else {
|
||||||
console.debug('error', res);
|
console.debug('error', res);
|
||||||
return {} as Options;
|
return {} as Options;
|
||||||
|
@ -49,7 +49,7 @@ const GetPeers: SearchProvider = async (o) => {
|
||||||
let res = await apiCall('vpn.wireguard.peers.list', {});
|
let res = await apiCall('vpn.wireguard.peers.list', {});
|
||||||
if (res.Error === null) {
|
if (res.Error === null) {
|
||||||
console.debug('peers', res.Data);
|
console.debug('peers', res.Data);
|
||||||
return Object.fromEntries(res.Data.map(r => [r.name, { display: r.name }]));
|
return Object.fromEntries(res.Data.map((r: any) => [r.name, { display: r.name }]));
|
||||||
} else {
|
} else {
|
||||||
console.debug('error', res);
|
console.debug('error', res);
|
||||||
return {} as Options;
|
return {} as Options;
|
||||||
|
@ -249,7 +249,7 @@ export const editTypes: { [key: string]: { [key: string]: any } } = {
|
||||||
'icmp': {
|
'icmp': {
|
||||||
display: 'ICMP',
|
display: 'ICMP',
|
||||||
fields: {
|
fields: {
|
||||||
icmp_code: { is: 'NumberBox', label: 'ICMP Code' },
|
code: { is: 'NumberBox', label: 'ICMP Code' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'group': {
|
'group': {
|
||||||
|
|
|
@ -68,7 +68,11 @@ th, td {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border: 0.125rem solid var(--cl-bg-el);
|
border: 0.125rem solid var(--cl-bg-el);
|
||||||
}
|
}
|
||||||
th > *{
|
td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
th>* {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { apiCall } from '../../api';
|
import { apiCall } from '../../api';
|
||||||
import getPlugins from '../../plugins';
|
import getPlugins from '../../plugins';
|
||||||
|
import EnumValueDisplay from '~/components/display/EnumValueDisplay.vue';
|
||||||
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
|
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
|
||||||
|
import ArrayDisplay from '~/components/display/ArrayDisplay.vue';
|
||||||
const p = getPlugins();
|
const p = getPlugins();
|
||||||
|
|
||||||
let addresses = $ref([]);
|
let addresses = $ref([]);
|
||||||
let loading = $ref(false);
|
let loading = $ref(false);
|
||||||
let selection = $ref([] as number[]);
|
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 = [
|
const columns = [
|
||||||
{ heading: 'Name', path: 'name' },
|
{ heading: 'Name', path: 'name' },
|
||||||
{ heading: 'Type', path: 'type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
|
{ heading: 'Type', path: 'address_type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
|
||||||
{ heading: 'Value', path: 'value' },
|
{ heading: 'Value', path: 'address_type', component: markRaw(EnumValueDisplay), componentProp: 'data', props: { definition: addressValueDefinition } },
|
||||||
{ heading: 'Comment', path: 'comment' },
|
{ heading: 'Comment', path: 'comment' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -33,35 +43,13 @@ const displayData = $computed(() => {
|
||||||
for (const index in addresses) {
|
for (const index in addresses) {
|
||||||
data.push({
|
data.push({
|
||||||
name: addresses[index].name,
|
name: addresses[index].name,
|
||||||
value: getAddressValue(addresses[index]),
|
address_type: addresses[index].address_type,
|
||||||
type: addresses[index].address_type,
|
|
||||||
comment: addresses[index].comment,
|
comment: addresses[index].comment,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data;
|
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(){
|
async function deleteAddress(){
|
||||||
let res = await apiCall('object.addresses_delete', { id: displayData[selection[0]].name });
|
let res = await apiCall('object.addresses_delete', { id: displayData[selection[0]].name });
|
||||||
if (res.Error === null) {
|
if (res.Error === null) {
|
||||||
|
|
|
@ -2,16 +2,26 @@
|
||||||
import { apiCall } from '../../api';
|
import { apiCall } from '../../api';
|
||||||
import getPlugins from '../../plugins';
|
import getPlugins from '../../plugins';
|
||||||
import EnumTypeDisplay from '~/components/display/EnumTypeDisplay.vue';
|
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();
|
const p = getPlugins();
|
||||||
|
|
||||||
let services = $ref({});
|
let services = $ref({});
|
||||||
let loading = $ref(false);
|
let loading = $ref(false);
|
||||||
let selection = $ref([] as number[]);
|
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 = [
|
const columns = [
|
||||||
{ heading: 'Name', path: 'name' },
|
{ heading: 'Name', path: 'name' },
|
||||||
{ heading: 'Type', path: 'type', component: markRaw(EnumTypeDisplay), componentProp: 'data' },
|
{ heading: 'Type', path: 'service_type', component: markRaw(EnumTypeDisplay), componentProp: 'data', props: { definition: serviceValueDefinition } },
|
||||||
{ heading: 'Value', path: 'value' },
|
{ heading: 'Value', path: 'service_type', component: markRaw(EnumValueDisplay), componentProp: 'data', props: { definition: serviceValueDefinition } },
|
||||||
{ heading: 'Comment', path: 'comment' },
|
{ heading: 'Comment', path: 'comment' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -21,38 +31,13 @@ const displayData = $computed(() => {
|
||||||
for (const index in services) {
|
for (const index in services) {
|
||||||
data.push({
|
data.push({
|
||||||
name: services[index].name,
|
name: services[index].name,
|
||||||
value: getServiceValue(services[index]),
|
service_type: services[index].service_type,
|
||||||
type: Object.keys(services[index].service_type)[0],
|
|
||||||
comment: services[index].comment,
|
comment: services[index].comment,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data;
|
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(){
|
async function load(){
|
||||||
loading = true;
|
loading = true;
|
||||||
let res = await apiCall('object.services.list', {});
|
let res = await apiCall('object.services.list', {});
|
||||||
|
|
|
@ -23,3 +23,9 @@ export function variantOf(enumValue: any) {
|
||||||
|
|
||||||
export type Index = string | number | symbol;
|
export type Index = string | number | symbol;
|
||||||
export type MaybeIndex = Index | null;
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue