Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/src/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@
"jsonObjectRequired": "Must be a JSON object",
"jsonObjectInvalid": "Please enter a valid JSON object",
"jsonFormatError": "Invalid JSON format",
"jsonFormatErrorWithMessage": "Invalid JSON format: {{message}}"
"jsonFormatErrorWithMessage": "Invalid JSON format: {{message}}",
"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)",
"invalidURL": "{{label}} format is incorrect. Please enter a valid HTTP/HTTPS link (e.g., http://example.com or https://example.com)",
"invalidJdbcURL": "{{label}} format is incorrect. Please enter a valid JDBC connection string (e.g., jdbc:mysql://localhost:3306/database)"
},
"placeholders": {
"enter": "Please enter",
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/i18n/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@
"jsonObjectRequired": "必须是JSON对象",
"jsonObjectInvalid": "请输入合法的JSON对象",
"jsonFormatError": "JSON格式错误",
"jsonFormatErrorWithMessage": "JSON格式错误:{{message}}"
"jsonFormatErrorWithMessage": "JSON格式错误:{{message}}",
"invalidIP": "{{label}}格式不正确,请输入有效的IP地址(如:192.168.1.1)或 IP:端口(如:192.168.1.1:3306)",
"invalidURL": "{{label}}格式不正确,请输入有效的HTTP/HTTPS链接(如:http://example.com 或 https://example.com)",
"invalidJdbcURL": "{{label}}格式不正确,请输入有效的JDBC连接字符串(如:jdbc:mysql://localhost:3306/database)"
},
"placeholders": {
"enter": "请输入",
Expand Down
104 changes: 104 additions & 0 deletions frontend/src/pages/DataCollection/Create/CreateTask.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,55 @@ import { useTranslation } from "react-i18next";

const { TextArea } = Input;

// IP 地址校验正则
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]?)$/;
// IP:端口 校验正则
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])$/;
// HTTP/HTTPS URL 校验正则
const URL_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&/=]*)$/i;
// JDBC URL 校验函数
const validateJdbcUrl = (url: string): boolean => {
// 基本格式检查
if (!url.startsWith('jdbc:')) return false;

// 检查是否包含 :// 或 :@ (Oracle格式)
const hasDoubleSlash = url.includes('://');
const hasAtSymbol = url.includes(':@');

if (!hasDoubleSlash && !hasAtSymbol) {
return false;
}

// 额外检查:不允许以 : 或 / 结尾
if (url.endsWith(':') || url.endsWith('/')) {
return false;
}

if (hasDoubleSlash) {
// 检查 // 后面的部分
const afterSlashMatch = url.match(/:\/\/([^/?]+)(\/|$)/);
if (!afterSlashMatch) return false;

const hostPort = afterSlashMatch[1];
if (!hostPort) return false;

// 如果有端口号,检查端口格式
if (hostPort.includes(':')) {
const parts = hostPort.split(':');
const host = parts[0];
const port = parts[1];

if (!host) return false;
if (!port || !/^\d{1,5}$/.test(port)) {
return false;
}
}
}

return true;
};


type CollectionTemplate = {
id: string;
name: string;
Expand Down Expand Up @@ -351,6 +400,61 @@ export default function CollectionTaskCreate() {
message: t("dataCollection.createTask.placeholders.enterWithLabel", { label }),
});
}

// 根据字段名判断是否需要特殊校验
const needsIPValidation = key === "ip";
const needsHTTPURLValidation = key === "endpoint" || key === "api";
const needsJDBCURLValidation = key === "jdbcUrl";

// 添加字段类型特定的校验
if (needsIPValidation) {
// IP 地址校验
rules.push({
validator: (_: any, value: any) => {
if (!value || value.trim() === "") {
return Promise.resolve();
}
const isValid = IP_REGEX.test(value) || IP_PORT_REGEX.test(value);
if (!isValid) {
return Promise.reject(
new Error(t("dataCollection.createTask.messages.invalidIP", { label }))
);
}
return Promise.resolve();
},
});
} else if (needsHTTPURLValidation) {
// HTTP/HTTPS URL 校验
rules.push({
validator: (_: any, value: any) => {
if (!value || value.trim() === "") {
return Promise.resolve();
}
if (!URL_REGEX.test(value)) {
return Promise.reject(
new Error(t("dataCollection.createTask.messages.invalidURL", { label }))
);
}
return Promise.resolve();
},
});
} else if (needsJDBCURLValidation) {
// JDBC URL 校验
rules.push({
validator: (_: any, value: any) => {
if (!value || value.trim() === "") {
return Promise.resolve();
}
if (!validateJdbcUrl(value)) {
return Promise.reject(
new Error(t("dataCollection.createTask.messages.invalidJdbcURL", { label }))
);
}
return Promise.resolve();
},
});
}

if (fieldType === "jsonobject") {
rules.push({
validator: (_: any, value: any) => {
Expand Down
Loading