5
5
6
6
<script setup lang="ts">
7
7
import { useI18n } from 'vue-i18n'
8
+ import { cloneDeep } from 'lodash-es'
8
9
import {
9
10
NeInlineNotification,
10
11
type NeComboboxOption,
@@ -20,6 +21,7 @@ import {
20
21
} from '@nethesis/vue-components'
21
22
import { ref, type PropType, watch, type Ref, computed } from 'vue'
22
23
import { ubusCall, ValidationError } from '@/lib/standalone/ubus'
24
+ import type { AxiosResponse } from 'axios'
23
25
import {
24
26
MessageBag,
25
27
validateAlphanumeric,
@@ -43,8 +45,21 @@ const props = defineProps({
43
45
}
44
46
})
45
47
48
+ type MatchInfo = {
49
+ database: string
50
+ family: 'ipv4' | 'ipv6'
51
+ id: string
52
+ name: string
53
+ type: string
54
+ }
55
+
56
+ type MatchInfoResponse = AxiosResponse<{
57
+ info: Record<string, MatchInfo>
58
+ }>
59
+
46
60
const emit = defineEmits(['close', 'reloadData'])
47
61
62
+ const portForwardsUsingHostSet = ref('')
48
63
const { t } = useI18n()
49
64
const name = ref('')
50
65
const nameRef = ref()
@@ -76,7 +91,13 @@ const ipVersionOptions = ref([
76
91
])
77
92
78
93
const recordOptionsButCurrent = computed(() => {
79
- return props.recordOptions?.filter((option) => option.id !== props.currentHostSet?.id)
94
+ // Filter out objects from recordOptions based on the presence of an IP address with a hyphen in allObjects
95
+ const objectsWithHyphenIp = props.allObjects
96
+ .filter((obj) => obj.ipaddr.some((ip: string) => ip.includes('-')))
97
+ .map((obj) => obj.id)
98
+ return props.recordOptions?.filter(
99
+ (option) => option.id !== props.currentHostSet?.id && !objectsWithHyphenIp.includes(option.id)
100
+ )
80
101
})
81
102
82
103
const allObjectsButCurrent = computed(() => {
@@ -94,7 +115,7 @@ watch(
94
115
// editing host or host set
95
116
name.value = props.currentHostSet.name
96
117
ipVersion.value = props.currentHostSet.family as IpVersion
97
- records.value = props.currentHostSet.ipaddr
118
+ records.value = cloneDeep( props.currentHostSet.ipaddr) // deep clone to avoid modifying the original array
98
119
} else {
99
120
// creating host or host set, reset form to defaults
100
121
name.value = ''
@@ -105,6 +126,18 @@ watch(
105
126
}
106
127
)
107
128
129
+ // compute portForwardsUsingHostSet the name of the portforward rule using this object
130
+ watch(
131
+ () => props.currentHostSet?.matches,
132
+ async (matches) => {
133
+ if (matches) {
134
+ portForwardsUsingHostSet.value = await getMatchedItemsName(matches)
135
+ } else {
136
+ portForwardsUsingHostSet.value = ''
137
+ }
138
+ }
139
+ )
140
+
108
141
function closeDrawer() {
109
142
emit('close')
110
143
}
@@ -133,6 +166,50 @@ function runFieldValidators(
133
166
return validators.every((validator) => validator.valid)
134
167
}
135
168
169
+ async function getMatchedItemsName(matches: string[]): Promise<string> {
170
+ try {
171
+ const res: MatchInfoResponse = await ubusCall('ns.objects', 'get-info', { ids: matches })
172
+ const names: string[] = []
173
+ for (const match of Object.values(res.data.info)) {
174
+ if (match.type == 'redirect') {
175
+ names.push(match.name)
176
+ }
177
+ }
178
+ return names.join(', ')
179
+ } catch (error: any) {
180
+ console.error('Error fetching getMatchedItemsName:', error)
181
+ return ''
182
+ }
183
+ }
184
+
185
+ function validateNoIpRangeWithPortForward(records: Array<string>) {
186
+ for (const record of records) {
187
+ if (record.includes('-') && portForwardsUsingHostSet.value) {
188
+ return {
189
+ valid: false,
190
+ errMessage: 'standalone.objects.range_not_compatible_with_port_forward'
191
+ }
192
+ }
193
+ }
194
+ return {
195
+ valid: true
196
+ }
197
+ }
198
+
199
+ function validateNoObjectsWithPortForward(records: Array<string>) {
200
+ for (const record of records) {
201
+ if (record.includes('objects/') && portForwardsUsingHostSet.value) {
202
+ return {
203
+ valid: false,
204
+ errMessage: 'standalone.objects.objects_are_not_compatible_with_port_forward'
205
+ }
206
+ }
207
+ }
208
+ return {
209
+ valid: true
210
+ }
211
+ }
212
+
136
213
function validateHostSetNotExists(value: string) {
137
214
if (allObjectsButCurrent.value?.find((obj) => obj.name === value && obj.subtype === 'host_set')) {
138
215
return {
@@ -158,7 +235,15 @@ function validate() {
158
235
nameRef
159
236
],
160
237
// records
161
- [[validateRequired(records.value[0])], 'ipaddr', recordRef]
238
+ [
239
+ [
240
+ validateNoObjectsWithPortForward(records.value),
241
+ validateNoIpRangeWithPortForward(records.value),
242
+ validateRequired(records.value[0])
243
+ ],
244
+ 'ipaddr',
245
+ recordRef
246
+ ]
162
247
]
163
248
164
249
// reset firstErrorRef for focus management
@@ -296,7 +381,11 @@ function deleteRecord(index: number) {
296
381
v-if="errorBag.getFirstI18nKeyFor('ipaddr')"
297
382
:class="'mt-2 text-sm text-rose-700 dark:text-rose-400'"
298
383
>
299
- {{ t(errorBag.getFirstI18nKeyFor('ipaddr')) }}
384
+ {{
385
+ t(errorBag.getFirstI18nKeyFor('ipaddr'), {
386
+ name: portForwardsUsingHostSet
387
+ })
388
+ }}
300
389
</p>
301
390
<NeButton class="mt-4" size="md" @click="addRecord" kind="secondary">
302
391
<template #prefix>
0 commit comments