Skip to content

Commit d2b6734

Browse files
authored
💄 add url validation (#402)
1 parent 97e2cfe commit d2b6734

File tree

3 files changed

+112
-2
lines changed

3 files changed

+112
-2
lines changed

frontend/src/i18n/locales/en/common.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,10 @@
225225
"jsonObjectRequired": "Must be a JSON object",
226226
"jsonObjectInvalid": "Please enter a valid JSON object",
227227
"jsonFormatError": "Invalid JSON format",
228-
"jsonFormatErrorWithMessage": "Invalid JSON format: {{message}}"
228+
"jsonFormatErrorWithMessage": "Invalid JSON format: {{message}}",
229+
"invalidIP": "{{label}} format is incorrect. Please enter a valid IP address (e.g., 192.168.1.1) or IP:Port (e.g., 192.168.1.1:3306)",
230+
"invalidURL": "{{label}} format is incorrect. Please enter a valid HTTP/HTTPS link (e.g., http://example.com or https://example.com)",
231+
"invalidJdbcURL": "{{label}} format is incorrect. Please enter a valid JDBC connection string (e.g., jdbc:mysql://localhost:3306/database)"
229232
},
230233
"placeholders": {
231234
"enter": "Please enter",

frontend/src/i18n/locales/zh/common.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,10 @@
225225
"jsonObjectRequired": "必须是JSON对象",
226226
"jsonObjectInvalid": "请输入合法的JSON对象",
227227
"jsonFormatError": "JSON格式错误",
228-
"jsonFormatErrorWithMessage": "JSON格式错误:{{message}}"
228+
"jsonFormatErrorWithMessage": "JSON格式错误:{{message}}",
229+
"invalidIP": "{{label}}格式不正确,请输入有效的IP地址(如:192.168.1.1)或 IP:端口(如:192.168.1.1:3306)",
230+
"invalidURL": "{{label}}格式不正确,请输入有效的HTTP/HTTPS链接(如:http://example.com 或 https://example.com)",
231+
"invalidJdbcURL": "{{label}}格式不正确,请输入有效的JDBC连接字符串(如:jdbc:mysql://localhost:3306/database)"
229232
},
230233
"placeholders": {
231234
"enter": "请输入",

frontend/src/pages/DataCollection/Create/CreateTask.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,55 @@ import { useTranslation } from "react-i18next";
1010

1111
const { TextArea } = Input;
1212

13+
// IP 地址校验正则
14+
const IP_REGEX = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
15+
// IP:端口 校验正则
16+
const IP_PORT_REGEX = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):([0-9]{1,5}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
17+
// HTTP/HTTPS URL 校验正则
18+
const URL_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&/=]*)$/i;
19+
// JDBC URL 校验函数
20+
const validateJdbcUrl = (url: string): boolean => {
21+
// 基本格式检查
22+
if (!url.startsWith('jdbc:')) return false;
23+
24+
// 检查是否包含 :// 或 :@ (Oracle格式)
25+
const hasDoubleSlash = url.includes('://');
26+
const hasAtSymbol = url.includes(':@');
27+
28+
if (!hasDoubleSlash && !hasAtSymbol) {
29+
return false;
30+
}
31+
32+
// 额外检查:不允许以 : 或 / 结尾
33+
if (url.endsWith(':') || url.endsWith('/')) {
34+
return false;
35+
}
36+
37+
if (hasDoubleSlash) {
38+
// 检查 // 后面的部分
39+
const afterSlashMatch = url.match(/:\/\/([^/?]+)(\/|$)/);
40+
if (!afterSlashMatch) return false;
41+
42+
const hostPort = afterSlashMatch[1];
43+
if (!hostPort) return false;
44+
45+
// 如果有端口号,检查端口格式
46+
if (hostPort.includes(':')) {
47+
const parts = hostPort.split(':');
48+
const host = parts[0];
49+
const port = parts[1];
50+
51+
if (!host) return false;
52+
if (!port || !/^\d{1,5}$/.test(port)) {
53+
return false;
54+
}
55+
}
56+
}
57+
58+
return true;
59+
};
60+
61+
1362
type CollectionTemplate = {
1463
id: string;
1564
name: string;
@@ -351,6 +400,61 @@ export default function CollectionTaskCreate() {
351400
message: t("dataCollection.createTask.placeholders.enterWithLabel", { label }),
352401
});
353402
}
403+
404+
// 根据字段名判断是否需要特殊校验
405+
const needsIPValidation = key === "ip";
406+
const needsHTTPURLValidation = key === "endpoint" || key === "api";
407+
const needsJDBCURLValidation = key === "jdbcUrl";
408+
409+
// 添加字段类型特定的校验
410+
if (needsIPValidation) {
411+
// IP 地址校验
412+
rules.push({
413+
validator: (_: any, value: any) => {
414+
if (!value || value.trim() === "") {
415+
return Promise.resolve();
416+
}
417+
const isValid = IP_REGEX.test(value) || IP_PORT_REGEX.test(value);
418+
if (!isValid) {
419+
return Promise.reject(
420+
new Error(t("dataCollection.createTask.messages.invalidIP", { label }))
421+
);
422+
}
423+
return Promise.resolve();
424+
},
425+
});
426+
} else if (needsHTTPURLValidation) {
427+
// HTTP/HTTPS URL 校验
428+
rules.push({
429+
validator: (_: any, value: any) => {
430+
if (!value || value.trim() === "") {
431+
return Promise.resolve();
432+
}
433+
if (!URL_REGEX.test(value)) {
434+
return Promise.reject(
435+
new Error(t("dataCollection.createTask.messages.invalidURL", { label }))
436+
);
437+
}
438+
return Promise.resolve();
439+
},
440+
});
441+
} else if (needsJDBCURLValidation) {
442+
// JDBC URL 校验
443+
rules.push({
444+
validator: (_: any, value: any) => {
445+
if (!value || value.trim() === "") {
446+
return Promise.resolve();
447+
}
448+
if (!validateJdbcUrl(value)) {
449+
return Promise.reject(
450+
new Error(t("dataCollection.createTask.messages.invalidJdbcURL", { label }))
451+
);
452+
}
453+
return Promise.resolve();
454+
},
455+
});
456+
}
457+
354458
if (fieldType === "jsonobject") {
355459
rules.push({
356460
validator: (_: any, value: any) => {

0 commit comments

Comments
 (0)