import { IExecuteSingleFunctions } from 'n8n-core'; import { IDataObject, INodeTypeDescription, INodeExecutionData, INodeType, } from 'n8n-workflow'; export class LinkFish implements INodeType { description: INodeTypeDescription = { displayName: 'link.fish Scrape', name: 'linkFish', icon: 'file:linkfish.png', group: ['input'], version: 1, description: 'Scrape data from an URL', defaults: { name: 'link.fish Scrape', color: '#33AA22', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'linkFishApi', required: true, } ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', options: [ { name: 'All Data', value: 'data', description: 'Get all found data', }, { name: 'Apps', value: 'apps', description: 'Get mobile app information', }, { name: 'Social Media', value: 'socialMedia', description: 'Get social-media profiles', }, { name: 'Screenshot', value: 'screenshot', description: 'Get screenshot', }, ], default: 'data', description: 'The operation to perform.', }, // ---------------------------------- // All // ---------------------------------- { displayName: 'URL', name: 'url', type: 'string', default: '', required: true, description: 'URL of which the data should be extracted.', }, // ---------------------------------- // data // ---------------------------------- { displayName: 'Item Format', name: 'itemFormat', type: 'options', options: [ { name: 'Normal', value: 'normal' }, { name: 'Flat', value: 'flat' }, ], default: 'flat', displayOptions: { show: { operation: [ 'data' ], }, }, description: 'If data should be returned in the "normal"
format or in the "flat" one which has all
items on one level.', }, { displayName: 'Simplify Types', name: 'simplifySpecialTypes', type: 'boolean', default: true, displayOptions: { show: { operation: [ 'data' ], }, }, description: 'If special types like "ParameterValue"
should be simplified to a key -> value pair.', }, // ---------------------------------- // screenshot // ---------------------------------- { displayName: 'Type', name: 'type', type: 'options', options: [ { name: 'Normal', value: 'normal', description: 'Creates a screenshot in 16:9 format.', }, { name: 'Full', value: 'full', description: 'Creates a full page screenshot.', }, ], default: 'normal', displayOptions: { show: { operation: [ 'screenshot' ], }, }, description: 'The file format the screenshot should be returned as.', }, { displayName: 'File Format', name: 'fileFormat', type: 'options', options: [ { name: 'JPG', value: 'jpg', }, { name: 'PNG', value: 'png', }, ], default: 'jpg', displayOptions: { show: { operation: [ 'screenshot' ], }, }, description: 'The file format the screenshot should be returned as.', }, { displayName: 'Width', name: 'width', type: 'number', typeOptions: { minValue: 50, maxValue: 1280, }, default: 640, displayOptions: { show: { operation: [ 'screenshot' ], }, }, description: 'The width of the screenshot in pixel.', }, { displayName: 'Binary Property', name: 'binaryPropertyName', type: 'string', default: 'data', required: true, displayOptions: { show: { operation: [ 'screenshot', ], }, }, placeholder: '', description: 'Name of the binary property in which to save
the binary data of the screenshot.', }, // ---------------------------------- // Multiple ones // ---------------------------------- { displayName: 'Browser Render', name: 'browserRender', type: 'boolean', default: false, displayOptions: { hide: { operation: [ 'screenshot' ], }, }, description: 'Renders the website in a browser
(charges 5 instead of 1 credit!)', }, { displayName: 'Return URLs', name: 'returnUrls', type: 'boolean', default: false, displayOptions: { hide: { operation: [ 'data', 'screenshot', ], }, }, description: 'Returns app URLs instead of the identifiers.', }, ], }; async executeSingle(this: IExecuteSingleFunctions): Promise { const url = this.getNodeParameter('url') as string; const credentials = this.getCredentials('linkFishApi'); if (credentials === undefined) { throw new Error('No credentials got returned!'); } let requestMethod = 'GET'; // For Post const body: IDataObject = { url, }; // For Query string const qs: IDataObject = {}; let endpoint: string; let encoding: string | null = 'utf8'; const operation = this.getNodeParameter('operation') as string; if (operation === 'data') { requestMethod = 'POST'; body.browser_render = this.getNodeParameter('browserRender') as boolean; body.item_format = this.getNodeParameter('itemFormat') as string; body.simplify_special_types = this.getNodeParameter('simplifySpecialTypes') as boolean; endpoint = 'data'; } else if (operation === 'apps') { requestMethod = 'GET'; qs.browser_render = this.getNodeParameter('browserRender') as boolean; qs.return_urls = this.getNodeParameter('returnUrls') as boolean; endpoint = 'apps'; } else if (operation === 'socialMedia') { requestMethod = 'GET'; qs.browser_render = this.getNodeParameter('browserRender') as boolean; qs.return_urls = this.getNodeParameter('returnUrls') as boolean; endpoint = 'social-media'; } else if (operation === 'screenshot') { requestMethod = 'GET'; encoding = null; qs.file_format = this.getNodeParameter('fileFormat') as string; qs.type = this.getNodeParameter('type') as string; qs.width = this.getNodeParameter('width') as number; endpoint = 'browser-screenshot'; } else { throw new Error(`The operation "${operation}" is not supported!`); } const options = { method: requestMethod, body, headers: { 'x-lf-source': 'n8n', }, qs, uri: `https://api.link.fish/Urls/${endpoint}`, auth: { user: credentials.email as string, pass: credentials.apiKey as string, }, encoding, json: true }; const responseData = await this.helpers.request(options); if (operation === 'screenshot') { const item = this.getInputData(); const newItem: INodeExecutionData = { json: item.json, binary: {}, }; if (item.binary !== undefined) { // Create a shallow copy of the binary data so that the old // data references which do not get changed still stay behind // but the incoming data does not get changed. Object.assign(newItem.binary, item.binary); } // Add the returned data to the item as binary property const binaryPropertyName = this.getNodeParameter('binaryPropertyName') as string; let fileExtension = 'png'; let mimeType = 'image/png'; if (qs.file_format === 'jpg') { fileExtension = 'jpg'; mimeType = 'image/jpeg'; } newItem.binary![binaryPropertyName] = await this.helpers.prepareBinaryData(responseData, `screenshot.${fileExtension}`, mimeType); return newItem; } return { json: responseData, }; } }