@@ -199,129 +199,125 @@ export function useAddConnectionForm(props: AddConnectionFormProps, emit: AddCon
|
||||
return ips;
|
||||
};
|
||||
|
||||
// Helper function to parse a single script line
|
||||
const parseScriptLine = (line: string): { type: 'SSH' | 'RDP' | 'VNC' | null, userHostPort: string, name: string | null, password: string | null, keyName: string | null, proxyName: string | null, tags: string[], note: string | null, error?: string } => {
|
||||
const firstSpaceIndex = line.indexOf(' ');
|
||||
let userHostPortPart = '';
|
||||
let remainingLine = line;
|
||||
|
||||
if (firstSpaceIndex !== -1) {
|
||||
userHostPortPart = line.substring(0, firstSpaceIndex);
|
||||
remainingLine = line.substring(firstSpaceIndex + 1).trim();
|
||||
} else {
|
||||
userHostPortPart = line;
|
||||
remainingLine = '';
|
||||
}
|
||||
|
||||
if (!userHostPortPart) return { type: null, userHostPort: '', name: null, password: null, keyName: null, proxyName: null, tags: [], note: null, error: t('connections.form.scriptErrorMissingHost', '缺少 user@host:port 部分') };
|
||||
// Helper function to parse a single script line using minimist
|
||||
|
||||
let type: 'SSH' | 'RDP' | 'VNC' | null = 'SSH';
|
||||
let name: string | null = null;
|
||||
|
||||
const parseScriptLine = (line: string): { type: 'SSH' | 'RDP' | 'VNC', userHostPort: string, name: string, password: string | null, keyName: string | null, proxyName: string | null, tags: string[], note: string | null, error?: string } => {
|
||||
line = line.trim();
|
||||
if (!line) {
|
||||
return { type: 'SSH', userHostPort: '', name: '', password: null, keyName: null, proxyName: null, tags: [], note: null, error: t('connections.form.scriptErrorEmptyLine', 'Input line cannot be empty') };
|
||||
}
|
||||
|
||||
// 1. Extract user@host:port
|
||||
const firstSpaceIndex = line.indexOf(' ');
|
||||
const userHostPortPart = firstSpaceIndex === -1 ? line : line.substring(0, firstSpaceIndex);
|
||||
const optionsString = firstSpaceIndex === -1 ? '' : line.substring(firstSpaceIndex + 1).trim();
|
||||
|
||||
// 2. Validate user@host:port (allow user@host without port)
|
||||
const userHostPortRegex = /^([^@\s]+)@([^:\s]+)(?::([0-9]+))?$/;
|
||||
const match = userHostPortPart.match(userHostPortRegex);
|
||||
if (!match) {
|
||||
return { type: 'SSH', userHostPort: userHostPortPart, name: '', password: null, keyName: null, proxyName: null, tags: [], note: null, error: t('connections.form.scriptErrorInvalidUserHostPortFormat', { part: userHostPortPart }) };
|
||||
}
|
||||
const [, user, host /*, portStr */] = match; // portStr not used for now
|
||||
const defaultName = `${user}@${host}`; // Default name
|
||||
|
||||
// 3. Initialize results and defaults
|
||||
let type: 'SSH' | 'RDP' | 'VNC' = 'SSH';
|
||||
let name: string = defaultName;
|
||||
let password: string | null = null;
|
||||
let keyName: string | null = null;
|
||||
let proxyName: string | null = null;
|
||||
const tags: string[] = [];
|
||||
let tags: string[] = [];
|
||||
let note: string | null = null;
|
||||
let currentArg: string | null = null;
|
||||
let noteParts: string[] = [];
|
||||
|
||||
const argRegex = /(-[^=\s]+)(?:\s+("(?:\\"|[^"])*"|[^-\s][^\s]*))?/g;
|
||||
let match;
|
||||
let lastIndex = 0;
|
||||
|
||||
while ((match = argRegex.exec(remainingLine)) !== null) {
|
||||
const arg = match[1];
|
||||
let value = match[2] || '';
|
||||
|
||||
if (value.startsWith('"') && value.endsWith('"')) {
|
||||
value = value.slice(1, -1).replace(/\\"/g, '"');
|
||||
}
|
||||
|
||||
if (noteParts.length > 0 && currentArg === '-note') {
|
||||
note = noteParts.join(' ');
|
||||
noteParts = [];
|
||||
}
|
||||
|
||||
currentArg = arg;
|
||||
|
||||
if (arg === '-tags') {
|
||||
// Tags handled later
|
||||
} else if (arg === '-note') {
|
||||
// Note handled later
|
||||
}
|
||||
|
||||
if (value) {
|
||||
switch (arg) {
|
||||
case '-type':
|
||||
const upperType = value.toUpperCase();
|
||||
if (upperType === 'SSH' || upperType === 'RDP' || upperType === 'VNC') {
|
||||
type = upperType as 'SSH' | 'RDP' | 'VNC';
|
||||
} else {
|
||||
return { type: null, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorInvalidType', { type: value }) };
|
||||
}
|
||||
currentArg = null;
|
||||
break;
|
||||
case '-name':
|
||||
name = value;
|
||||
currentArg = null;
|
||||
break;
|
||||
case '-p':
|
||||
password = value;
|
||||
currentArg = null;
|
||||
break;
|
||||
case '-k':
|
||||
keyName = value;
|
||||
currentArg = null;
|
||||
break;
|
||||
case '-proxy':
|
||||
proxyName = value;
|
||||
currentArg = null;
|
||||
break;
|
||||
case '-tags':
|
||||
tags.push(value);
|
||||
break;
|
||||
case '-note':
|
||||
noteParts.push(value);
|
||||
break;
|
||||
default:
|
||||
if (currentArg === '-note') {
|
||||
noteParts.push(value);
|
||||
} else {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorUnknownArg', { arg: currentArg }) };
|
||||
}
|
||||
}
|
||||
}
|
||||
lastIndex = argRegex.lastIndex;
|
||||
}
|
||||
|
||||
const remainingPart = remainingLine.substring(lastIndex).trim();
|
||||
if (remainingPart) {
|
||||
if (currentArg === '-tags') {
|
||||
const tagRegex = /("(?:\\"|[^"])*"|[^\s"]+)/g;
|
||||
let tagMatch;
|
||||
while ((tagMatch = tagRegex.exec(remainingPart)) !== null) {
|
||||
let tag = tagMatch[1];
|
||||
if (tag.startsWith('"') && tag.endsWith('"')) {
|
||||
tag = tag.slice(1, -1).replace(/\\"/g, '"');
|
||||
// 4. Parse optionsString
|
||||
// Regex to split by space, respecting quotes
|
||||
const args = optionsString.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
|
||||
let i = 0;
|
||||
while (i < args.length) {
|
||||
const arg = args[i];
|
||||
if (arg.startsWith('-')) {
|
||||
const key = arg.substring(1).toLowerCase();
|
||||
i++; // Move to the expected position of the value
|
||||
|
||||
if (key === 'tags') {
|
||||
// Handle -tags, which can be followed by zero or more tags
|
||||
tags = [];
|
||||
while (i < args.length && !args[i].startsWith('-')) {
|
||||
tags.push(args[i].replace(/^"|"$/g, '')); // Remove surrounding quotes
|
||||
i++;
|
||||
}
|
||||
tags.push(tag);
|
||||
// No need to i++ here, the next loop iteration or outer loop handles it
|
||||
} else if (key === 'note') {
|
||||
// Handle -note, which consumes the rest of the line
|
||||
const noteParts = [];
|
||||
while (i < args.length) {
|
||||
noteParts.push(args[i]);
|
||||
i++;
|
||||
}
|
||||
note = noteParts.join(' ').replace(/^"|"$/g, ''); // Join parts and remove quotes
|
||||
break; // Exit the outer loop as note consumes the rest
|
||||
} else if (i >= args.length) {
|
||||
// All other options require a value
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorMissingValueForKey', { key: arg }) };
|
||||
} else {
|
||||
// Handle options that require a single value
|
||||
const value = args[i].replace(/^"|"$/g, ''); // Remove surrounding quotes
|
||||
switch (key) {
|
||||
case 'type':
|
||||
const typeValue = value.toUpperCase();
|
||||
if (typeValue === 'SSH' || typeValue === 'RDP' || typeValue === 'VNC') {
|
||||
type = typeValue;
|
||||
} else {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorInvalidType', { value: args[i] }) };
|
||||
}
|
||||
break;
|
||||
case 'name':
|
||||
name = value;
|
||||
break;
|
||||
case 'p': // password
|
||||
password = value;
|
||||
break;
|
||||
case 'k': // key name
|
||||
keyName = value;
|
||||
break;
|
||||
case 'proxy':
|
||||
proxyName = value;
|
||||
break;
|
||||
default:
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorUnknownOption', { option: arg }) };
|
||||
}
|
||||
i++; // Move past the value
|
||||
}
|
||||
} else if (currentArg === '-note') {
|
||||
noteParts.push(remainingPart);
|
||||
} else {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorUnexpectedToken', { token: remainingPart }) };
|
||||
// Arguments after user@host:port must start with '-'
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorUnexpectedArgument', { argument: arg }) };
|
||||
}
|
||||
}
|
||||
|
||||
if (noteParts.length > 0 && currentArg === '-note') {
|
||||
note = noteParts.join(' ');
|
||||
// 5. Validation based on type
|
||||
if (type === 'SSH') {
|
||||
if (!password && !keyName) {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorMissingAuthForSsh') };
|
||||
}
|
||||
// Allow both password and key, handle precedence in handleScriptModeSubmit
|
||||
} else if (type === 'RDP') {
|
||||
if (!password) {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorMissingPasswordForRdp') };
|
||||
}
|
||||
if (keyName) {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorKeyNotApplicableForRdp') };
|
||||
}
|
||||
} else if (type === 'VNC') {
|
||||
if (!password) {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorMissingPasswordForVnc') };
|
||||
}
|
||||
if (keyName) {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorKeyNotApplicableForVnc') };
|
||||
}
|
||||
}
|
||||
|
||||
const userHostPortRegex = /^[^@]+@[^:]+(:[0-9]+)?$/;
|
||||
if (!userHostPortRegex.test(userHostPortPart)) {
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note, error: t('connections.form.scriptErrorInvalidUserHostPort', { part: userHostPortPart })};
|
||||
}
|
||||
|
||||
return { type, userHostPort: userHostPortPart, name, password, keyName, proxyName, tags, note };
|
||||
};
|
||||
|
||||
@@ -421,14 +417,22 @@ export function useAddConnectionForm(props: AddConnectionFormProps, emit: AddCon
|
||||
if (connData.tag_names && connData.tag_names.length > 0) {
|
||||
const tagIds = [];
|
||||
for (const tagName of connData.tag_names) {
|
||||
const foundTag = tags.value.find(t_ => t_.name === tagName); // Renamed t to t_ to avoid conflict
|
||||
if (foundTag) {
|
||||
tagIds.push(foundTag.id);
|
||||
} else {
|
||||
uiNotificationsStore.showError(t('connections.form.scriptErrorTagNotFound', { tagName }));
|
||||
resolutionErrorOccurred = true;
|
||||
break;
|
||||
let foundTag = tags.value.find(t_ => t_.name === tagName); // Renamed t to t_ to avoid conflict
|
||||
if (!foundTag) {
|
||||
// 自动创建不存在的标签
|
||||
const newTag = await tagsStore.addTag(tagName);
|
||||
if (newTag) {
|
||||
foundTag = newTag;
|
||||
uiNotificationsStore.showInfo(t('connections.form.scriptTagCreated', { tagName }));
|
||||
// 确保标签列表已更新
|
||||
await tagsStore.fetchTags();
|
||||
} else {
|
||||
uiNotificationsStore.showError(t('connections.form.scriptErrorTagCreationFailed', { tagName }));
|
||||
resolutionErrorOccurred = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tagIds.push(foundTag.id);
|
||||
}
|
||||
if (resolutionErrorOccurred) break;
|
||||
connData.tag_ids = tagIds;
|
||||
|
||||
Reference in New Issue
Block a user