UNPKG

n8n-nodes-linkupapi

Version:

n8n node for LINKUP API - Complete LinkedIn automation with 100% API coverage and full documentation

251 lines (247 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Linkup = void 0; const types_1 = require("./types"); const utils_1 = require("./utils"); const properties_1 = require("./properties"); const authentication_1 = require("./categories/authentication"); const profile_1 = require("./categories/profile"); const company_1 = require("./categories/company"); const network_1 = require("./categories/network"); const message_1 = require("./categories/message"); const post_1 = require("./categories/post"); const recruiter_1 = require("./categories/recruiter"); const signal_1 = require("./categories/signal"); const companyApi_1 = require("./categories/companyApi"); const personApi_1 = require("./categories/personApi"); const multiRequests_1 = require("./categories/multiRequests"); class Linkup { constructor() { this.description = { displayName: "Linkup API for LinkedIn", name: "linkup", icon: "file:linkup.svg", group: ["transform"], version: 1, description: "Automate LinkedIn with Linkup", defaults: { name: "LINKUP", color: "#0077b5", }, inputs: ["main"], outputs: ["main"], credentials: [ { name: "linkupApi", }, ], properties: properties_1.nodeProperties, }; } // === MAIN EXECUTION METHOD === async execute() { var _a, _b, _c, _d; const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { const resource = this.getNodeParameter("resource", i); const operation = this.getNodeParameter("operation", i); let requestOptions = {}; try { const timeout = 30000; // Default timeout // Get credentials const creds = await utils_1.LinkupUtils.getCredentialsWithFallback(this); // Build the request body according to the category let body = {}; // Add login token if necessary const operationsNotRequiringLoginToken = [ "login", "verifyCode", "extractPostReactions", "extractPostComments", "extractProfileReactions", "extractProfileComments", "extractProfilePosts", "extractCompanyPosts", "searchCompaniesApi", "getCompanyInfoApi", "getCompanyInfoByDomain", "searchProfilesApi", "extractProfileInfoApi", "profileEnrichment", "extractCompanyEmployees", ]; const requiresLoginToken = !operationsNotRequiringLoginToken.includes(operation); console.log("🔐 LINKUP Auth Debug:", { operation, requiresLoginToken, hasLoginToken: !!creds.loginToken, loginTokenLength: ((_a = creds.loginToken) === null || _a === void 0 ? void 0 : _a.length) || 0 }); if (creds.loginToken && requiresLoginToken) { body.login_token = creds.loginToken; } else if (requiresLoginToken && !creds.loginToken) { console.warn("⚠️ LINKUP Warning: Operation requires login_token but none provided in credentials. This will likely result in 403 error."); } // Build the body according to the category switch (resource) { case "authentication": body = await authentication_1.AuthenticationOperations.buildRequestBody(this, i, operation); break; case "profile": body = await profile_1.ProfileOperations.buildRequestBody(this, i, operation); break; case "company": body = await company_1.CompanyOperations.buildRequestBody(this, i, operation); break; case "network": body = await network_1.NetworkOperations.buildRequestBody(this, i, operation); break; case "message": body = await message_1.MessageOperations.buildRequestBody(this, i, operation); break; case "post": body = await post_1.PostOperations.buildRequestBody(this, i, operation); break; case "recruiter": body = await recruiter_1.RecruiterOperations.buildRequestBody(this, i, operation); break; case "signal": body = await signal_1.SignalOperations.buildRequestBody(this, i, operation); break; case "companyApi": body = await companyApi_1.CompanyApiOperations.buildRequestBody(this, i, operation); break; case "personApi": body = await personApi_1.PersonApiOperations.buildRequestBody(this, i, operation); break; case "multiRequests": body = await multiRequests_1.MultiRequestsOperations.buildRequestBody(this, i, operation); break; } // Add default country if not specified if (!body.country) { body.country = "FR"; } // Get endpoint let endpoint = utils_1.LinkupUtils.getEndpointForOperation(operation); // Pour Multi-Requests, utiliser directement l'URL fournie if (resource === "multiRequests") { // Ajouter automatiquement l'API key dans les headers const defaultHeaders = { "x-api-key": creds.apiKey, "Content-Type": "application/json", "User-Agent": "n8n-linkup-node/1.2.0", }; requestOptions = { method: body.method, url: body.url, headers: { ...defaultHeaders, ...(body.headers || {}) }, body: body.requestBody || {}, timeout: timeout, }; } else { // Pour les autres ressources, utiliser l'endpoint normal requestOptions = utils_1.LinkupUtils.buildRequestOptions(endpoint, "POST", creds.apiKey, body, timeout); } // Debug logging pour les erreurs 403 console.log("🔄 LINKUP Request debug:", { resource, operation, url: requestOptions.url, method: requestOptions.method, hasApiKey: !!requestOptions.headers["x-api-key"], apiKeyPrefix: ((_b = requestOptions.headers["x-api-key"]) === null || _b === void 0 ? void 0 : _b.substring(0, 8)) + "...", hasLoginToken: !!creds.loginToken, loginTokenInBody: !!body.login_token, }); const response = await this.helpers.httpRequest(requestOptions); const result = { json: { _debug: { requestBody: body, requestHeaders: requestOptions.headers, endpoint: endpoint, apiResponse: response, }, ...response, _meta: { resource, operation, timestamp: new Date().toISOString(), nodeVersion: types_1.NODE_VERSION, }, }, pairedItem: { item: i }, }; returnData.push(result); } catch (error) { console.error("🚨 LINKUP Request failed:", { error: error.message, statusCode: error.statusCode, resource, operation, url: requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.url, }); // Message d'erreur spécifique pour 403 let errorMessage = error.message; if (error.statusCode === 403) { // Operations nécessitant un login_token (même logique que plus haut) const operationsNotRequiringLoginToken = [ "login", "verifyCode", "extractPostReactions", "extractPostComments", "extractProfileReactions", "extractProfileComments", "extractProfilePosts", "extractCompanyPosts", "searchCompaniesApi", "getCompanyInfoApi", "getCompanyInfoByDomain", "searchProfilesApi", "extractProfileInfoApi", "profileEnrichment", "extractCompanyEmployees", ]; const requiresLoginToken = !operationsNotRequiringLoginToken.includes(operation); if (requiresLoginToken) { errorMessage = `❌ Access forbidden (403): This operation requires a LinkedIn login token. 📋 Step-by-step solution: 1. First run "Authentication → login" with your LinkedIn email/password 2. Copy the "login_token" from the response 3. Go to your Linkup credentials and paste it in "Login Token (Optional)" field 4. Save credentials and retry this operation 🔍 Current status: Resource: ${resource}, Operation: ${operation} 💡 The login token authenticates you with LinkedIn to access your personal data.`; } else { errorMessage = `❌ Access forbidden (403): Your API key may be invalid, expired, or you may not have sufficient credits/permissions for this operation. 🔍 Details: Resource: ${resource}, Operation: ${operation} 💡 This is a public operation that should only require a valid API key.`; } } returnData.push({ json: { error: errorMessage, originalError: error.message, statusCode: error.statusCode, response: (_c = error.response) === null || _c === void 0 ? void 0 : _c.body, resource, operation, debug: { hasApiKey: !!((_d = requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.headers) === null || _d === void 0 ? void 0 : _d["x-api-key"]), url: requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.url, } }, pairedItem: { item: i }, }); } } return [returnData]; } } exports.Linkup = Linkup;