Skip to content

Commit 205f12e

Browse files
authored
feat: add display name support for SMS alerts and enhance validation (#12945)
1 parent aca94e4 commit 205f12e

4 files changed

Lines changed: 100 additions & 10 deletions

File tree

frontend/src/api/interface/alert.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export namespace Alert {
212212
title: string;
213213
status: string;
214214
config: {
215+
displayName?: string;
215216
phone?: string;
216217
alertDailyNum?: number;
217218
};

frontend/src/views/setting/alert/dash/task/index.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,9 @@ function checkIPs(rule: any, value: any, callback: any) {
647647
if (item === '') {
648648
continue;
649649
}
650+
if (item.includes('0.0.0.0') || item.includes('::')) {
651+
return callback(new Error(i18n.global.t('firewall.addressFormatError')));
652+
}
650653
if (item.indexOf('/') !== -1) {
651654
if (item.indexOf(':') !== -1) {
652655
if (checkCidrV6(item)) {

frontend/src/views/setting/alert/setting/drawer/index.vue

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@
7676
</template>
7777

7878
<template v-else-if="form.type === 'sms'">
79-
<el-form-item :label="$t('xpack.alert.phone')" prop="smsPhone" :rules="[Rules.phone]">
80-
<el-input clearable v-model="form.smsPhone" />
79+
<el-form-item :label="$t('xpack.alert.displayName')" prop="smsDisplayName">
80+
<el-input v-model.trim="form.smsDisplayName" />
81+
<span class="input-help">{{ $t('xpack.alert.displayNameHelper') }}</span>
82+
</el-form-item>
83+
<el-form-item :label="$t('xpack.alert.phone')" prop="smsPhone">
84+
<el-input clearable v-model.trim="form.smsPhone" />
8185
<span class="input-help">{{ $t('xpack.alert.phoneHelper') }}</span>
8286
</el-form-item>
83-
<el-form-item
84-
:label="$t('xpack.alert.dailyAlertNum')"
85-
prop="smsDailyAlertNum"
86-
:rules="[Rules.integerNumber, checkNumberRange(20, 100)]"
87-
>
87+
<el-form-item :label="$t('xpack.alert.dailyAlertNum')" prop="smsDailyAlertNum">
8888
<el-input clearable v-model.number="form.smsDailyAlertNum" min="20" max="100" />
8989
<span class="input-help">{{ $t('xpack.alert.dailyAlertNumHelper') }}</span>
9090
</el-form-item>
@@ -154,14 +154,20 @@ const emailRules = {
154154
recipient: [Rules.requiredInput],
155155
};
156156
157+
const smsRules = {
158+
smsDisplayName: [Rules.requiredInput, { validator: checkSmsDisplayNameDuplicate, trigger: 'blur' }],
159+
smsPhone: [Rules.requiredInput, Rules.phone, { validator: checkPhoneDuplicate, trigger: 'blur' }],
160+
smsDailyAlertNum: [Rules.integerNumber, checkNumberRange(20, 100)],
161+
};
162+
157163
const webhookRules = {
158164
webhookName: [Rules.requiredInput, { validator: checkDisplayNameDuplicate, trigger: 'blur' }],
159165
webhookUrl: [Rules.requiredInput],
160166
};
161167
162168
const currentRules = computed(() => {
163169
if (form.type === 'email') return emailRules;
164-
if (form.type === 'sms') return {};
170+
if (form.type === 'sms') return smsRules;
165171
return webhookRules;
166172
});
167173
@@ -199,11 +205,14 @@ const formRef = ref<FormInstance>();
199205
const alertConfigs = ref<Alert.AlertConfigInfo[]>([]);
200206
201207
const loadAlertConfigs = async () => {
208+
loading.value = true;
202209
try {
203210
const res = await ListAlertConfigs();
204211
alertConfigs.value = res.data?.filter((item: Alert.AlertConfigInfo) => item.type !== 'common') || [];
205212
} catch {
206213
alertConfigs.value = [];
214+
} finally {
215+
loading.value = false;
207216
}
208217
};
209218
@@ -217,6 +226,7 @@ const form = reactive({
217226
recipient: '',
218227
webhookName: '',
219228
webhookUrl: '',
229+
smsDisplayName: '',
220230
smsPhone: '',
221231
smsDailyAlertNum: 50,
222232
});
@@ -260,6 +270,66 @@ function checkDisplayNameDuplicate(_rule: unknown, value: string, callback: (err
260270
callback();
261271
}
262272
273+
function checkSmsDisplayNameDuplicate(_rule: unknown, value: string, callback: (error?: Error) => void) {
274+
const currentValue = normalizeDisplayName(value);
275+
if (!currentValue) {
276+
callback();
277+
return;
278+
}
279+
280+
const duplicated = alertConfigs.value.some((item) => {
281+
if (item.type !== 'sms') {
282+
return false;
283+
}
284+
if (form.id && item.id === form.id) {
285+
return false;
286+
}
287+
try {
288+
const config = JSON.parse(item.config || '{}') as { displayName?: string };
289+
return normalizeDisplayName(config.displayName) === currentValue;
290+
} catch {
291+
return false;
292+
}
293+
});
294+
295+
if (duplicated) {
296+
callback(new Error(i18n.global.t('commons.rule.duplicate')));
297+
return;
298+
}
299+
300+
callback();
301+
}
302+
303+
function checkPhoneDuplicate(_rule: unknown, value: string, callback: (error?: Error) => void) {
304+
const currentValue = normalizeDisplayName(value);
305+
if (!currentValue) {
306+
callback();
307+
return;
308+
}
309+
310+
const duplicated = alertConfigs.value.some((item) => {
311+
if (item.type !== 'sms') {
312+
return false;
313+
}
314+
if (form.id && item.id === form.id) {
315+
return false;
316+
}
317+
try {
318+
const config = JSON.parse(item.config || '{}') as { phone?: string };
319+
return normalizeDisplayName(config.phone) === currentValue;
320+
} catch {
321+
return false;
322+
}
323+
});
324+
325+
if (duplicated) {
326+
callback(new Error(i18n.global.t('commons.rule.duplicate')));
327+
return;
328+
}
329+
330+
callback();
331+
}
332+
263333
const titleMap: Record<string, string> = {
264334
email: 'xpack.alert.emailConfig',
265335
weCom: 'xpack.alert.weCom',
@@ -290,6 +360,7 @@ const acceptParams = (params: DrawerProps): void => {
290360
form.config = { ...defaultEmailForm, ...(params.config || {}) };
291361
form.recipient = params.config?.recipient || '';
292362
} else if (form.type === 'sms') {
363+
form.smsDisplayName = params.config?.displayName || '';
293364
form.smsPhone = params.config?.phone || '';
294365
form.smsDailyAlertNum = params.config?.alertDailyNum || 50;
295366
} else {
@@ -305,6 +376,7 @@ const acceptParams = (params: DrawerProps): void => {
305376
form.updateUser = '';
306377
form.config = { ...defaultEmailForm };
307378
form.recipient = '';
379+
form.smsDisplayName = '';
308380
form.webhookName = '';
309381
form.webhookUrl = '';
310382
form.smsPhone = '';
@@ -322,6 +394,7 @@ const onTypeChange = (type: string) => {
322394
form.config = { ...defaultEmailForm };
323395
form.recipient = '';
324396
} else if (type === 'sms') {
397+
form.smsDisplayName = '';
325398
form.smsPhone = '';
326399
form.smsDailyAlertNum = 50;
327400
} else {
@@ -348,7 +421,11 @@ const buildSavePayload = () => {
348421
};
349422
}
350423
if (form.type === 'sms') {
351-
const configInfo = { phone: form.smsPhone, alertDailyNum: form.smsDailyAlertNum };
424+
const configInfo = {
425+
displayName: form.smsDisplayName,
426+
phone: form.smsPhone,
427+
alertDailyNum: form.smsDailyAlertNum,
428+
};
352429
return {
353430
id: form.id,
354431
type: 'sms',
@@ -432,6 +509,15 @@ watch(
432509
},
433510
);
434511
512+
watch(
513+
() => [form.smsDisplayName, form.smsPhone, form.smsDailyAlertNum],
514+
() => {
515+
if (form.type === 'sms') {
516+
formRef.value?.clearValidate(['smsDisplayName', 'smsPhone']);
517+
}
518+
},
519+
);
520+
435521
const handleClose = () => {
436522
isOK.value = false;
437523
drawerVisible.value = false;

frontend/src/views/setting/safe/allowips/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ function checkIPs(rule: any, value: any, callback: any) {
3636
if (item === '') {
3737
continue;
3838
}
39-
if (item === '0.0.0.0' || item === '::') {
39+
if (item.includes('0.0.0.0') || item.includes('::')) {
4040
return callback(new Error(i18n.global.t('firewall.addressFormatError')));
4141
}
4242
if (item.indexOf('/') !== -1) {

0 commit comments

Comments
 (0)