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
JavaScript
;
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;