mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
319 lines
8.6 KiB
TypeScript
319 lines
8.6 KiB
TypeScript
import { NodeApiError, type IExecuteFunctions, type INode } from 'n8n-workflow';
|
|
import { NodeOperationError } from 'n8n-workflow';
|
|
|
|
import { SESSION_MODE } from './actions/common/fields';
|
|
import {
|
|
ERROR_MESSAGES,
|
|
DEFAULT_TIMEOUT_MINUTES,
|
|
MIN_TIMEOUT_MINUTES,
|
|
MAX_TIMEOUT_MINUTES,
|
|
INTEGRATION_URL,
|
|
} from './constants';
|
|
import { apiRequest } from './transport';
|
|
import type { IAirtopResponse } from './transport/types';
|
|
|
|
/**
|
|
* Validate a required string field
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @param field - The field to validate
|
|
* @param fieldName - The name of the field
|
|
*/
|
|
export function validateRequiredStringField(
|
|
this: IExecuteFunctions,
|
|
index: number,
|
|
field: string,
|
|
fieldName: string,
|
|
) {
|
|
let value = this.getNodeParameter(field, index) as string;
|
|
value = (value || '').trim();
|
|
const errorMessage = ERROR_MESSAGES.REQUIRED_PARAMETER.replace('{{field}}', fieldName);
|
|
|
|
if (!value) {
|
|
throw new NodeOperationError(this.getNode(), errorMessage, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Validate the session ID parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The validated session ID
|
|
*/
|
|
export function validateSessionId(this: IExecuteFunctions, index: number) {
|
|
let sessionId = this.getNodeParameter('sessionId', index, '') as string;
|
|
sessionId = (sessionId || '').trim();
|
|
|
|
if (!sessionId) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.SESSION_ID_REQUIRED, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return sessionId;
|
|
}
|
|
|
|
/**
|
|
* Validate the session ID and window ID parameters
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The validated session ID and window ID parameters
|
|
*/
|
|
export function validateSessionAndWindowId(this: IExecuteFunctions, index: number) {
|
|
let sessionId = this.getNodeParameter('sessionId', index, '') as string;
|
|
let windowId = this.getNodeParameter('windowId', index, '') as string;
|
|
sessionId = (sessionId || '').trim();
|
|
windowId = (windowId || '').trim();
|
|
|
|
if (!sessionId) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.SESSION_ID_REQUIRED, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
if (!windowId) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.WINDOW_ID_REQUIRED, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return {
|
|
sessionId,
|
|
windowId,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Validate the profile name parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The validated profile name
|
|
*/
|
|
export function validateProfileName(this: IExecuteFunctions, index: number) {
|
|
let profileName = this.getNodeParameter('profileName', index) as string;
|
|
profileName = (profileName || '').trim();
|
|
|
|
if (!profileName) {
|
|
return profileName;
|
|
}
|
|
|
|
if (!/^[a-zA-Z0-9-]+$/.test(profileName)) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.PROFILE_NAME_INVALID, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return profileName;
|
|
}
|
|
|
|
/**
|
|
* Validate the timeout minutes parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The validated timeout minutes
|
|
*/
|
|
export function validateTimeoutMinutes(this: IExecuteFunctions, index: number) {
|
|
const timeoutMinutes = this.getNodeParameter(
|
|
'timeoutMinutes',
|
|
index,
|
|
DEFAULT_TIMEOUT_MINUTES,
|
|
) as number;
|
|
|
|
if (timeoutMinutes < MIN_TIMEOUT_MINUTES || timeoutMinutes > MAX_TIMEOUT_MINUTES) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.TIMEOUT_MINUTES_INVALID, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return timeoutMinutes;
|
|
}
|
|
|
|
/**
|
|
* Validate the URL parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The validated URL
|
|
*/
|
|
export function validateUrl(this: IExecuteFunctions, index: number) {
|
|
let url = this.getNodeParameter('url', index) as string;
|
|
url = (url || '').trim();
|
|
|
|
if (!url) {
|
|
return '';
|
|
}
|
|
|
|
if (!url.startsWith('http')) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.URL_INVALID, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Validate the Proxy URL parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @param proxy - The value of the Proxy parameter
|
|
* @returns The validated proxy URL
|
|
*/
|
|
export function validateProxyUrl(this: IExecuteFunctions, index: number, proxy: string) {
|
|
let proxyUrl = this.getNodeParameter('proxyUrl', index, '') as string;
|
|
proxyUrl = (proxyUrl || '').trim();
|
|
|
|
// only validate proxyUrl if proxy is custom
|
|
if (proxy !== 'custom') {
|
|
return '';
|
|
}
|
|
|
|
if (!proxyUrl) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.PROXY_URL_REQUIRED, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
if (!proxyUrl.startsWith('http')) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.PROXY_URL_INVALID, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return proxyUrl;
|
|
}
|
|
|
|
/**
|
|
* Validate the screen resolution parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The validated screen resolution
|
|
*/
|
|
export function validateScreenResolution(this: IExecuteFunctions, index: number) {
|
|
let screenResolution = this.getNodeParameter('screenResolution', index, '') as string;
|
|
screenResolution = (screenResolution || '').trim().toLowerCase();
|
|
const regex = /^\d{3,4}x\d{3,4}$/; // Expected format: 1280x720
|
|
|
|
if (!screenResolution) {
|
|
return '';
|
|
}
|
|
|
|
if (!regex.test(screenResolution)) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.SCREEN_RESOLUTION_INVALID, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return screenResolution;
|
|
}
|
|
|
|
/**
|
|
* Validate the save profile on termination parameter
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @param profileName - The profile name
|
|
* @returns The validated save profile on termination
|
|
*/
|
|
export function validateSaveProfileOnTermination(
|
|
this: IExecuteFunctions,
|
|
index: number,
|
|
profileName: string,
|
|
) {
|
|
const saveProfileOnTermination = this.getNodeParameter(
|
|
'saveProfileOnTermination',
|
|
index,
|
|
false,
|
|
) as boolean;
|
|
|
|
if (saveProfileOnTermination && !profileName) {
|
|
throw new NodeOperationError(this.getNode(), ERROR_MESSAGES.PROFILE_NAME_REQUIRED, {
|
|
itemIndex: index,
|
|
});
|
|
}
|
|
|
|
return saveProfileOnTermination;
|
|
}
|
|
|
|
/**
|
|
* Check if there is an error in the API response and throw NodeApiError
|
|
* @param node - The node instance
|
|
* @param response - The response from the API
|
|
*/
|
|
export function validateAirtopApiResponse(node: INode, response: IAirtopResponse) {
|
|
if (response?.errors?.length) {
|
|
const errorMessage = response.errors.map((error) => error.message).join('\n');
|
|
throw new NodeApiError(node, {
|
|
message: errorMessage,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a screenshot from the API response to a binary buffer
|
|
* @param screenshot - The screenshot from the API response
|
|
* @returns The processed screenshot
|
|
*/
|
|
export function convertScreenshotToBinary(screenshot: { dataUrl: string }): Buffer {
|
|
const base64Data = screenshot.dataUrl.replace('data:image/jpeg;base64,', '');
|
|
const buffer = Buffer.from(base64Data, 'base64');
|
|
return buffer;
|
|
}
|
|
|
|
/**
|
|
* Check if a new session should be created
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns True if a new session should be created, false otherwise
|
|
*/
|
|
export function shouldCreateNewSession(this: IExecuteFunctions, index: number) {
|
|
const sessionMode = this.getNodeParameter('sessionMode', index) as string;
|
|
return Boolean(sessionMode && sessionMode === SESSION_MODE.NEW);
|
|
}
|
|
|
|
/**
|
|
* Create a new session and window
|
|
* @param this - The execution context
|
|
* @param index - The index of the node
|
|
* @returns The session ID and window ID
|
|
*/
|
|
export async function createSessionAndWindow(
|
|
this: IExecuteFunctions,
|
|
index: number,
|
|
): Promise<{ sessionId: string; windowId: string }> {
|
|
const node = this.getNode();
|
|
const noCodeEndpoint = `${INTEGRATION_URL}/create-session`;
|
|
const profileName = validateProfileName.call(this, index);
|
|
const url = validateRequiredStringField.call(this, index, 'url', 'URL');
|
|
|
|
const { sessionId } = await apiRequest.call(this, 'POST', noCodeEndpoint, {
|
|
configuration: {
|
|
profileName,
|
|
},
|
|
});
|
|
|
|
if (!sessionId) {
|
|
throw new NodeApiError(node, {
|
|
message: 'Failed to create session',
|
|
code: 500,
|
|
});
|
|
}
|
|
this.logger.info(`[${node.name}] Session successfully created.`);
|
|
|
|
const windowResponse = await apiRequest.call(this, 'POST', `/sessions/${sessionId}/windows`, {
|
|
url,
|
|
});
|
|
const windowId = windowResponse?.data?.windowId as string;
|
|
|
|
if (!windowId) {
|
|
throw new NodeApiError(node, {
|
|
message: 'Failed to create window',
|
|
code: 500,
|
|
});
|
|
}
|
|
this.logger.info(`[${node.name}] Window successfully created.`);
|
|
return { sessionId, windowId };
|
|
}
|