n8n-nodes-salla
Version:
n8n node for Salla.sa e-commerce platform integration with OAuth2.0
288 lines (287 loc) • 10.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SallaOAuth2Api = void 0;
class SallaOAuth2Api {
constructor() {
this.name = 'sallaOAuth2Api';
this.extends = ['oAuth2Api'];
this.displayName = 'Salla OAuth2 API';
this.documentationUrl = 'https://docs.salla.dev/';
this.icon = 'file:salla.svg';
this.properties = [
{
displayName: 'Grant Type',
name: 'grantType',
type: 'hidden',
default: 'authorizationCode',
},
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden',
default: 'https://accounts.salla.sa/oauth2/auth',
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden',
default: 'https://accounts.salla.sa/oauth2/token',
},
{
displayName: 'Scope',
name: 'scope',
type: 'hidden',
default: 'offline_access',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden',
default: '',
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden',
default: 'body',
},
{
displayName: 'Environment',
name: 'environment',
type: 'options',
options: [
{
name: 'Production',
value: 'production',
},
{
name: 'Sandbox',
value: 'sandbox',
},
],
default: 'production',
description: 'Select the Salla environment to connect to',
},
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
default: '',
required: true,
description: 'Your Salla application Client ID from Salla Partner Dashboard',
placeholder: 'e.g., 123456789-abcd-1234-efgh-123456789012',
},
{
displayName: 'Client Secret',
name: 'clientSecret',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
description: 'Your Salla application Client Secret from Salla Partner Dashboard',
placeholder: 'Your secret client key...',
},
{
displayName: 'OAuth Scopes',
name: 'oauthScopes',
type: 'string',
default: 'offline_access',
description: 'OAuth scopes to request. Common scopes: offline_access, read, write, orders, products, customers',
placeholder: 'offline_access',
},
{
displayName: 'Webhook Secret',
name: 'webhookSecret',
type: 'string',
typeOptions: {
password: true,
},
default: '',
description: 'Webhook secret for validating incoming webhooks from Salla (optional)',
placeholder: 'Your webhook secret...',
},
{
displayName: 'Store Domain',
name: 'storeDomain',
type: 'string',
default: '',
placeholder: 'your-store.salla.sa',
description: 'Your Salla store domain (optional - will be auto-detected from token)',
},
{
displayName: 'API Version',
name: 'apiVersion',
type: 'options',
options: [
{
name: 'v2 (Recommended)',
value: 'v2',
},
{
name: 'v1',
value: 'v1',
},
],
default: 'v2',
description: 'Salla API version to use',
},
{
displayName: 'Rate Limit Handling',
name: 'rateLimitHandling',
type: 'options',
options: [
{
name: 'Retry (Recommended)',
value: 'retry',
},
{
name: 'Fail',
value: 'fail',
},
],
default: 'retry',
description: 'How to handle rate limit responses from Salla API',
},
{
displayName: 'Max Retries',
name: 'maxRetries',
type: 'number',
default: 3,
typeOptions: {
minValue: 1,
maxValue: 10,
},
description: 'Maximum number of retries for rate limited requests',
displayOptions: {
show: {
rateLimitHandling: ['retry'],
},
},
},
{
displayName: 'Auto Refresh Tokens',
name: 'autoRefreshTokens',
type: 'boolean',
default: true,
description: 'Automatically refresh access tokens when they expire (considering 14-day Salla token expiry)',
},
{
displayName: 'Token Refresh Buffer (Minutes)',
name: 'tokenRefreshBuffer',
type: 'number',
default: 30,
typeOptions: {
minValue: 5,
maxValue: 1440, // 24 hours
},
description: 'Refresh tokens this many minutes before they expire',
displayOptions: {
show: {
autoRefreshTokens: [true],
},
},
},
];
this.test = {
request: {
baseURL: '={{$credentials.environment === "sandbox" ? "https://api.s-cart.com" : "https://api.salla.dev"}}/admin/{{$credentials.apiVersion}}',
url: '/profile',
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
},
rules: [
{
type: 'responseSuccessBody',
properties: {
key: 'status',
value: 200,
},
},
],
};
}
async preAuthentication(credentials) {
// Set the correct URLs based on environment
const isSandbox = credentials.environment === 'sandbox';
const scope = credentials.oauthScopes || 'offline_access';
return {
...credentials,
authUrl: isSandbox
? 'https://accounts.s-cart.com/oauth2/auth'
: 'https://accounts.salla.sa/oauth2/auth',
accessTokenUrl: isSandbox
? 'https://accounts.s-cart.com/oauth2/token'
: 'https://accounts.salla.sa/oauth2/token',
scope,
};
}
async authenticate(credentials) {
// This method is called after successful OAuth flow
// The oauthTokenData will be automatically saved by n8n
// Add timestamp for token expiry tracking
if (credentials.oauthTokenData) {
const tokenData = credentials.oauthTokenData;
if (tokenData.expires_in && !tokenData.expires_at) {
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + parseInt(tokenData.expires_in));
tokenData.expires_at = expiresAt.toISOString();
}
// Store additional metadata
tokenData.obtained_at = new Date().toISOString();
tokenData.environment = credentials.environment;
}
return credentials;
}
async testCredential(credentials) {
try {
// Basic validation
if (!credentials.clientId || !credentials.clientSecret) {
return {
status: 'Error',
message: 'Client ID and Client Secret are required',
};
}
// Check if OAuth token exists
if (!credentials.oauthTokenData) {
return {
status: 'Error',
message: 'OAuth authentication required. Please complete the OAuth flow.',
};
}
const tokenData = credentials.oauthTokenData;
if (!tokenData.access_token) {
return {
status: 'Error',
message: 'No access token found. Please re-authenticate.',
};
}
// Check token expiry
if (tokenData.expires_at) {
const expiresAt = new Date(tokenData.expires_at);
const now = new Date();
if (expiresAt <= now) {
return {
status: 'Error',
message: 'Access token has expired. Please re-authenticate.',
};
}
}
return {
status: 'OK',
message: 'Authentication successful',
};
}
catch (error) {
return {
status: 'Error',
message: `Authentication test failed: ${error.message || 'Unknown error'}`,
};
}
}
}
exports.SallaOAuth2Api = SallaOAuth2Api;