@pakoor/n8n-nodes-instagram 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +271 -0
- package/dist/__tests__/helpers/index.d.ts +2 -0
- package/dist/__tests__/helpers/index.d.ts.map +1 -0
- package/dist/__tests__/helpers/index.js +18 -0
- package/dist/__tests__/helpers/index.js.map +1 -0
- package/dist/__tests__/helpers/testUtils.d.ts +104 -0
- package/dist/__tests__/helpers/testUtils.d.ts.map +1 -0
- package/dist/__tests__/helpers/testUtils.js +175 -0
- package/dist/__tests__/helpers/testUtils.js.map +1 -0
- package/dist/credentials/InstagramAccessTokenApi.credentials.d.ts +19 -0
- package/dist/credentials/InstagramAccessTokenApi.credentials.d.ts.map +1 -0
- package/dist/credentials/InstagramAccessTokenApi.credentials.js +130 -0
- package/dist/credentials/InstagramAccessTokenApi.credentials.js.map +1 -0
- package/dist/credentials/InstagramOAuth2Api.credentials.d.ts +20 -0
- package/dist/credentials/InstagramOAuth2Api.credentials.d.ts.map +1 -0
- package/dist/credentials/InstagramOAuth2Api.credentials.js +177 -0
- package/dist/credentials/InstagramOAuth2Api.credentials.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/Instagram/Instagram.node.d.ts +10 -0
- package/dist/nodes/Instagram/Instagram.node.d.ts.map +1 -0
- package/dist/nodes/Instagram/Instagram.node.js +1152 -0
- package/dist/nodes/Instagram/Instagram.node.js.map +1 -0
- package/dist/nodes/Instagram/InstagramTrigger.node.d.ts +61 -0
- package/dist/nodes/Instagram/InstagramTrigger.node.d.ts.map +1 -0
- package/dist/nodes/Instagram/InstagramTrigger.node.js +315 -0
- package/dist/nodes/Instagram/InstagramTrigger.node.js.map +1 -0
- package/dist/nodes/Instagram/instagram.svg +15 -0
- package/dist/services/InstagramApiClient.d.ts +261 -0
- package/dist/services/InstagramApiClient.d.ts.map +1 -0
- package/dist/services/InstagramApiClient.js +548 -0
- package/dist/services/InstagramApiClient.js.map +1 -0
- package/dist/services/TokenManager.d.ts +73 -0
- package/dist/services/TokenManager.d.ts.map +1 -0
- package/dist/services/TokenManager.js +150 -0
- package/dist/services/TokenManager.js.map +1 -0
- package/dist/services/ValidationUtils.d.ts +44 -0
- package/dist/services/ValidationUtils.d.ts.map +1 -0
- package/dist/services/ValidationUtils.js +203 -0
- package/dist/services/ValidationUtils.js.map +1 -0
- package/dist/services/WebhookHandler.d.ts +206 -0
- package/dist/services/WebhookHandler.d.ts.map +1 -0
- package/dist/services/WebhookHandler.js +261 -0
- package/dist/services/WebhookHandler.js.map +1 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +21 -0
- package/dist/services/index.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TokenManager Service
|
|
4
|
+
* Handles token exchange and refresh for Instagram OAuth2 authentication
|
|
5
|
+
*
|
|
6
|
+
* Requirements:
|
|
7
|
+
* - 1.3: Exchange short-lived token for long-lived token (60 days validity)
|
|
8
|
+
* - 1.4: Auto-refresh when token is older than 24h AND expiring within 7 days
|
|
9
|
+
* - 1.5: Fallback to OAuth token exchange when refresh fails
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TokenManager = void 0;
|
|
13
|
+
class TokenManager {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.baseUrl = options?.baseUrl ?? 'https://graph.facebook.com';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Exchange short-lived token for long-lived token (60 days validity)
|
|
19
|
+
* Requirement 1.3: WHEN a short-lived token is received, THE OAuth2_Credential
|
|
20
|
+
* SHALL automatically exchange it for a long-lived token (60 days validity)
|
|
21
|
+
*
|
|
22
|
+
* @param shortLivedToken - The short-lived access token (1 hour validity)
|
|
23
|
+
* @param appSecret - The Facebook App Secret
|
|
24
|
+
* @returns Long-lived token response with 60 days validity
|
|
25
|
+
*/
|
|
26
|
+
async exchangeForLongLivedToken(shortLivedToken, appSecret) {
|
|
27
|
+
const url = new URL(`${this.baseUrl}/${TokenManager.GRAPH_API_VERSION}/oauth/access_token`);
|
|
28
|
+
url.searchParams.set('grant_type', 'fb_exchange_token');
|
|
29
|
+
url.searchParams.set('client_secret', appSecret);
|
|
30
|
+
url.searchParams.set('fb_exchange_token', shortLivedToken);
|
|
31
|
+
const response = await fetch(url.toString(), {
|
|
32
|
+
method: 'GET',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
const errorData = (await response.json().catch(() => ({})));
|
|
39
|
+
const errorMessage = errorData?.error?.message ?? 'Failed to exchange token';
|
|
40
|
+
throw new Error(`Token exchange failed: ${errorMessage}`);
|
|
41
|
+
}
|
|
42
|
+
const data = (await response.json());
|
|
43
|
+
return {
|
|
44
|
+
access_token: data.access_token,
|
|
45
|
+
token_type: data.token_type ?? 'bearer',
|
|
46
|
+
expires_in: data.expires_in ?? 5184000, // Default 60 days
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Refresh long-lived token (must be at least 24h old)
|
|
51
|
+
* Requirement 1.4: WHEN a long-lived token is older than 24 hours AND expiring
|
|
52
|
+
* within 7 days, THE OAuth2_Credential SHALL automatically refresh the token
|
|
53
|
+
*
|
|
54
|
+
* @param longLivedToken - The current long-lived access token
|
|
55
|
+
* @returns New long-lived token response
|
|
56
|
+
*/
|
|
57
|
+
async refreshLongLivedToken(longLivedToken) {
|
|
58
|
+
const url = new URL(`${this.baseUrl}/${TokenManager.GRAPH_API_VERSION}/oauth/access_token`);
|
|
59
|
+
url.searchParams.set('grant_type', 'fb_exchange_token');
|
|
60
|
+
url.searchParams.set('fb_exchange_token', longLivedToken);
|
|
61
|
+
const response = await fetch(url.toString(), {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const errorData = (await response.json().catch(() => ({})));
|
|
69
|
+
const errorMessage = errorData?.error?.message ?? 'Failed to refresh token';
|
|
70
|
+
throw new Error(`Token refresh failed: ${errorMessage}`);
|
|
71
|
+
}
|
|
72
|
+
const data = (await response.json());
|
|
73
|
+
return {
|
|
74
|
+
access_token: data.access_token,
|
|
75
|
+
token_type: data.token_type ?? 'bearer',
|
|
76
|
+
expires_in: data.expires_in ?? 5184000, // Default 60 days
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if token needs refresh (expiring within 7 days AND older than 24 hours)
|
|
81
|
+
* Requirement 1.4: Token should be refreshed when it's older than 24 hours
|
|
82
|
+
* AND expiring within 7 days
|
|
83
|
+
*
|
|
84
|
+
* @param expiresAt - Token expiration timestamp in milliseconds
|
|
85
|
+
* @param createdAt - Token creation timestamp in milliseconds (optional)
|
|
86
|
+
* @returns true if token should be refreshed
|
|
87
|
+
*/
|
|
88
|
+
shouldRefreshToken(expiresAt, createdAt) {
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
const sevenDaysInMs = TokenManager.REFRESH_THRESHOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
91
|
+
const twentyFourHoursInMs = TokenManager.MIN_TOKEN_AGE_HOURS * 60 * 60 * 1000;
|
|
92
|
+
// Check if token is expiring within 7 days
|
|
93
|
+
const isExpiringWithin7Days = expiresAt - now <= sevenDaysInMs;
|
|
94
|
+
// If createdAt is provided, check if token is older than 24 hours
|
|
95
|
+
if (createdAt !== undefined) {
|
|
96
|
+
const isOlderThan24Hours = now - createdAt >= twentyFourHoursInMs;
|
|
97
|
+
return isExpiringWithin7Days && isOlderThan24Hours;
|
|
98
|
+
}
|
|
99
|
+
// If no createdAt, only check expiry (assume token is old enough)
|
|
100
|
+
return isExpiringWithin7Days;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get valid token (auto-refresh if needed)
|
|
104
|
+
* Requirement 1.4 & 1.5: Auto-refresh token when needed, with fallback
|
|
105
|
+
*
|
|
106
|
+
* @param credentials - The OAuth2 credentials object
|
|
107
|
+
* @returns Valid access token
|
|
108
|
+
*/
|
|
109
|
+
async getValidToken(credentials) {
|
|
110
|
+
// If we have a long-lived token, check if it needs refresh
|
|
111
|
+
if (credentials.longLivedToken && credentials.longLivedTokenExpiresAt) {
|
|
112
|
+
const needsRefresh = this.shouldRefreshToken(credentials.longLivedTokenExpiresAt, credentials.longLivedTokenCreatedAt);
|
|
113
|
+
if (!needsRefresh) {
|
|
114
|
+
// Token is still valid, return it
|
|
115
|
+
return credentials.longLivedToken;
|
|
116
|
+
}
|
|
117
|
+
// Token needs refresh - try to refresh it
|
|
118
|
+
try {
|
|
119
|
+
const refreshedToken = await this.refreshLongLivedToken(credentials.longLivedToken);
|
|
120
|
+
return refreshedToken.access_token;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Requirement 1.5: Fallback to exchanging current OAuth token
|
|
124
|
+
if (credentials.accessToken && credentials.clientSecret) {
|
|
125
|
+
try {
|
|
126
|
+
const exchangedToken = await this.exchangeForLongLivedToken(credentials.accessToken, credentials.clientSecret);
|
|
127
|
+
return exchangedToken.access_token;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Both refresh and exchange failed
|
|
131
|
+
throw new Error('Token refresh failed and fallback exchange also failed. Please re-authenticate.');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
throw new Error('Token refresh failed. Please re-authenticate.');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// No long-lived token - try to exchange short-lived token
|
|
138
|
+
if (credentials.accessToken && credentials.clientSecret) {
|
|
139
|
+
const exchangedToken = await this.exchangeForLongLivedToken(credentials.accessToken, credentials.clientSecret);
|
|
140
|
+
return exchangedToken.access_token;
|
|
141
|
+
}
|
|
142
|
+
// No valid tokens available
|
|
143
|
+
throw new Error('No valid token available. Please authenticate first.');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.TokenManager = TokenManager;
|
|
147
|
+
TokenManager.REFRESH_THRESHOLD_DAYS = 7;
|
|
148
|
+
TokenManager.MIN_TOKEN_AGE_HOURS = 24;
|
|
149
|
+
TokenManager.GRAPH_API_VERSION = 'v18.0';
|
|
150
|
+
//# sourceMappingURL=TokenManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenManager.js","sourceRoot":"","sources":["../../src/services/TokenManager.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAuCH,MAAa,YAAY;IAOvB,YAAY,OAA8B;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,4BAA4B,CAAC;IAClE,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,yBAAyB,CAC7B,eAAuB,EACvB,SAAiB;QAEjB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC,iBAAiB,qBAAqB,CAAC,CAAC;QAC5F,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;QACxD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;QACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QAE3D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA2B,CAAC;YACtF,MAAM,YAAY,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,IAAI,0BAA0B,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;QAE/D,OAAO;YACL,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,QAAQ;YACvC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,OAAO,EAAE,kBAAkB;SAC3D,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,qBAAqB,CAAC,cAAsB;QAChD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC,iBAAiB,qBAAqB,CAAC,CAAC;QAC5F,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;QACxD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;QAE1D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA2B,CAAC;YACtF,MAAM,YAAY,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,IAAI,yBAAyB,CAAC;YAC5E,MAAM,IAAI,KAAK,CAAC,yBAAyB,YAAY,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;QAE/D,OAAO;YACL,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,QAAQ;YACvC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,OAAO,EAAE,kBAAkB;SAC3D,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,kBAAkB,CAAC,SAAiB,EAAE,SAAkB;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,aAAa,GAAG,YAAY,CAAC,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAChF,MAAM,mBAAmB,GAAG,YAAY,CAAC,mBAAmB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAE9E,2CAA2C;QAC3C,MAAM,qBAAqB,GAAG,SAAS,GAAG,GAAG,IAAI,aAAa,CAAC;QAE/D,kEAAkE;QAClE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,kBAAkB,GAAG,GAAG,GAAG,SAAS,IAAI,mBAAmB,CAAC;YAClE,OAAO,qBAAqB,IAAI,kBAAkB,CAAC;QACrD,CAAC;QAED,kEAAkE;QAClE,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa,CAAC,WAAwC;QAC1D,2DAA2D;QAC3D,IAAI,WAAW,CAAC,cAAc,IAAI,WAAW,CAAC,uBAAuB,EAAE,CAAC;YACtE,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,CAC1C,WAAW,CAAC,uBAAuB,EACnC,WAAW,CAAC,uBAAuB,CACpC,CAAC;YAEF,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,kCAAkC;gBAClC,OAAO,WAAW,CAAC,cAAc,CAAC;YACpC,CAAC;YAED,0CAA0C;YAC1C,IAAI,CAAC;gBACH,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;gBACpF,OAAO,cAAc,CAAC,YAAY,CAAC;YACrC,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;gBAC9D,IAAI,WAAW,CAAC,WAAW,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;oBACxD,IAAI,CAAC;wBACH,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,yBAAyB,CACzD,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,YAAY,CACzB,CAAC;wBACF,OAAO,cAAc,CAAC,YAAY,CAAC;oBACrC,CAAC;oBAAC,MAAM,CAAC;wBACP,mCAAmC;wBACnC,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;oBACJ,CAAC;gBACH,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAED,0DAA0D;QAC1D,IAAI,WAAW,CAAC,WAAW,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;YACxD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,yBAAyB,CACzD,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,YAAY,CACzB,CAAC;YACF,OAAO,cAAc,CAAC,YAAY,CAAC;QACrC,CAAC;QAED,4BAA4B;QAC5B,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;;AAxKH,oCAyKC;AAxKyB,mCAAsB,GAAG,CAAC,CAAC;AAC3B,gCAAmB,GAAG,EAAE,CAAC;AACzB,8BAAiB,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities for Instagram API inputs
|
|
3
|
+
* Validates inputs according to Instagram Graph API constraints
|
|
4
|
+
*/
|
|
5
|
+
import type { IButton, IQuickReply, IUserTag } from './InstagramApiClient';
|
|
6
|
+
export interface IValidationResult {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validates text message length (max 1000 characters)
|
|
12
|
+
* Requirements: 3.2
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateTextMessage(text: string): IValidationResult;
|
|
15
|
+
/**
|
|
16
|
+
* Validates media URL is HTTPS
|
|
17
|
+
* Requirements: 4.4
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateMediaUrl(url: string): IValidationResult;
|
|
20
|
+
/**
|
|
21
|
+
* Validates button template constraints
|
|
22
|
+
* - Max 3 buttons
|
|
23
|
+
* - Max 20 characters per button title
|
|
24
|
+
* Requirements: 5.1, 5.2
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateButtons(buttons: IButton[]): IValidationResult;
|
|
27
|
+
/**
|
|
28
|
+
* Validates quick replies constraints (max 13 items)
|
|
29
|
+
* Requirements: 5.4, 5.5
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateQuickReplies(quickReplies: IQuickReply[]): IValidationResult;
|
|
32
|
+
/**
|
|
33
|
+
* Validates user tags constraints
|
|
34
|
+
* - Max 20 tags
|
|
35
|
+
* - x, y positions must be between 0 and 1
|
|
36
|
+
* Requirements: 8.4, 8.5
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateUserTags(tags: IUserTag[]): IValidationResult;
|
|
39
|
+
/**
|
|
40
|
+
* Validates carousel items count (2-10 items)
|
|
41
|
+
* Requirements: 9.1, 9.2, 9.3
|
|
42
|
+
*/
|
|
43
|
+
export declare function validateCarouselItems<T>(items: T[]): IValidationResult;
|
|
44
|
+
//# sourceMappingURL=ValidationUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValidationUtils.d.ts","sourceRoot":"","sources":["../../src/services/ValidationUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE3E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAanE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAsB/D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAiDrE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,iBAAiB,CAuCnF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CA2CpE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,iBAAiB,CAoBtE"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Validation Utilities for Instagram API inputs
|
|
4
|
+
* Validates inputs according to Instagram Graph API constraints
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.validateTextMessage = validateTextMessage;
|
|
8
|
+
exports.validateMediaUrl = validateMediaUrl;
|
|
9
|
+
exports.validateButtons = validateButtons;
|
|
10
|
+
exports.validateQuickReplies = validateQuickReplies;
|
|
11
|
+
exports.validateUserTags = validateUserTags;
|
|
12
|
+
exports.validateCarouselItems = validateCarouselItems;
|
|
13
|
+
/**
|
|
14
|
+
* Validates text message length (max 1000 characters)
|
|
15
|
+
* Requirements: 3.2
|
|
16
|
+
*/
|
|
17
|
+
function validateTextMessage(text) {
|
|
18
|
+
if (typeof text !== 'string') {
|
|
19
|
+
return { valid: false, error: 'Message text must be a string' };
|
|
20
|
+
}
|
|
21
|
+
if (text.length > 1000) {
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
error: `Message text exceeds 1000 character limit (${text.length} characters)`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { valid: true };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validates media URL is HTTPS
|
|
31
|
+
* Requirements: 4.4
|
|
32
|
+
*/
|
|
33
|
+
function validateMediaUrl(url) {
|
|
34
|
+
if (typeof url !== 'string') {
|
|
35
|
+
return { valid: false, error: 'Media URL must be a string' };
|
|
36
|
+
}
|
|
37
|
+
if (!url.trim()) {
|
|
38
|
+
return { valid: false, error: 'Media URL cannot be empty' };
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const parsedUrl = new URL(url);
|
|
42
|
+
if (parsedUrl.protocol !== 'https:') {
|
|
43
|
+
return {
|
|
44
|
+
valid: false,
|
|
45
|
+
error: `Media URL must use HTTPS protocol (got ${parsedUrl.protocol.replace(':', '')})`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return { valid: false, error: 'Invalid URL format' };
|
|
51
|
+
}
|
|
52
|
+
return { valid: true };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validates button template constraints
|
|
56
|
+
* - Max 3 buttons
|
|
57
|
+
* - Max 20 characters per button title
|
|
58
|
+
* Requirements: 5.1, 5.2
|
|
59
|
+
*/
|
|
60
|
+
function validateButtons(buttons) {
|
|
61
|
+
if (!Array.isArray(buttons)) {
|
|
62
|
+
return { valid: false, error: 'Buttons must be an array' };
|
|
63
|
+
}
|
|
64
|
+
if (buttons.length === 0) {
|
|
65
|
+
return { valid: false, error: 'At least one button is required' };
|
|
66
|
+
}
|
|
67
|
+
if (buttons.length > 3) {
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
error: `Button count exceeds maximum of 3 (got ${buttons.length})`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
74
|
+
const button = buttons[i];
|
|
75
|
+
if (!button.title || typeof button.title !== 'string') {
|
|
76
|
+
return { valid: false, error: `Button ${i + 1}: title is required and must be a string` };
|
|
77
|
+
}
|
|
78
|
+
if (button.title.length > 20) {
|
|
79
|
+
return {
|
|
80
|
+
valid: false,
|
|
81
|
+
error: `Button ${i + 1}: title exceeds 20 character limit (${button.title.length} characters)`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (!['web_url', 'postback'].includes(button.type)) {
|
|
85
|
+
return {
|
|
86
|
+
valid: false,
|
|
87
|
+
error: `Button ${i + 1}: type must be 'web_url' or 'postback'`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (button.type === 'web_url' && button.url) {
|
|
91
|
+
const urlValidation = validateMediaUrl(button.url);
|
|
92
|
+
if (!urlValidation.valid) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
error: `Button ${i + 1}: ${urlValidation.error}`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { valid: true };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validates quick replies constraints (max 13 items)
|
|
104
|
+
* Requirements: 5.4, 5.5
|
|
105
|
+
*/
|
|
106
|
+
function validateQuickReplies(quickReplies) {
|
|
107
|
+
if (!Array.isArray(quickReplies)) {
|
|
108
|
+
return { valid: false, error: 'Quick replies must be an array' };
|
|
109
|
+
}
|
|
110
|
+
if (quickReplies.length === 0) {
|
|
111
|
+
return { valid: false, error: 'At least one quick reply is required' };
|
|
112
|
+
}
|
|
113
|
+
if (quickReplies.length > 13) {
|
|
114
|
+
return {
|
|
115
|
+
valid: false,
|
|
116
|
+
error: `Quick reply count exceeds maximum of 13 (got ${quickReplies.length})`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
for (let i = 0; i < quickReplies.length; i++) {
|
|
120
|
+
const qr = quickReplies[i];
|
|
121
|
+
if (!qr.title || typeof qr.title !== 'string') {
|
|
122
|
+
return { valid: false, error: `Quick reply ${i + 1}: title is required and must be a string` };
|
|
123
|
+
}
|
|
124
|
+
if (!qr.payload || typeof qr.payload !== 'string') {
|
|
125
|
+
return { valid: false, error: `Quick reply ${i + 1}: payload is required and must be a string` };
|
|
126
|
+
}
|
|
127
|
+
if (qr.image_url) {
|
|
128
|
+
const urlValidation = validateMediaUrl(qr.image_url);
|
|
129
|
+
if (!urlValidation.valid) {
|
|
130
|
+
return {
|
|
131
|
+
valid: false,
|
|
132
|
+
error: `Quick reply ${i + 1}: ${urlValidation.error}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { valid: true };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Validates user tags constraints
|
|
141
|
+
* - Max 20 tags
|
|
142
|
+
* - x, y positions must be between 0 and 1
|
|
143
|
+
* Requirements: 8.4, 8.5
|
|
144
|
+
*/
|
|
145
|
+
function validateUserTags(tags) {
|
|
146
|
+
if (!Array.isArray(tags)) {
|
|
147
|
+
return { valid: false, error: 'User tags must be an array' };
|
|
148
|
+
}
|
|
149
|
+
if (tags.length > 20) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
error: `User tag count exceeds maximum of 20 (got ${tags.length})`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
for (let i = 0; i < tags.length; i++) {
|
|
156
|
+
const tag = tags[i];
|
|
157
|
+
if (!tag.username || typeof tag.username !== 'string') {
|
|
158
|
+
return { valid: false, error: `User tag ${i + 1}: username is required and must be a string` };
|
|
159
|
+
}
|
|
160
|
+
if (typeof tag.x !== 'number' || isNaN(tag.x)) {
|
|
161
|
+
return { valid: false, error: `User tag ${i + 1}: x position must be a number` };
|
|
162
|
+
}
|
|
163
|
+
if (typeof tag.y !== 'number' || isNaN(tag.y)) {
|
|
164
|
+
return { valid: false, error: `User tag ${i + 1}: y position must be a number` };
|
|
165
|
+
}
|
|
166
|
+
if (tag.x < 0 || tag.x > 1) {
|
|
167
|
+
return {
|
|
168
|
+
valid: false,
|
|
169
|
+
error: `User tag ${i + 1}: x position must be between 0 and 1 (got ${tag.x})`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (tag.y < 0 || tag.y > 1) {
|
|
173
|
+
return {
|
|
174
|
+
valid: false,
|
|
175
|
+
error: `User tag ${i + 1}: y position must be between 0 and 1 (got ${tag.y})`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { valid: true };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Validates carousel items count (2-10 items)
|
|
183
|
+
* Requirements: 9.1, 9.2, 9.3
|
|
184
|
+
*/
|
|
185
|
+
function validateCarouselItems(items) {
|
|
186
|
+
if (!Array.isArray(items)) {
|
|
187
|
+
return { valid: false, error: 'Carousel items must be an array' };
|
|
188
|
+
}
|
|
189
|
+
if (items.length < 2) {
|
|
190
|
+
return {
|
|
191
|
+
valid: false,
|
|
192
|
+
error: `Carousel requires at least 2 items (got ${items.length})`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (items.length > 10) {
|
|
196
|
+
return {
|
|
197
|
+
valid: false,
|
|
198
|
+
error: `Carousel exceeds maximum of 10 items (got ${items.length})`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { valid: true };
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=ValidationUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValidationUtils.js","sourceRoot":"","sources":["../../src/services/ValidationUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebhookHandler Service
|
|
3
|
+
* Handles Instagram webhook verification, signature validation, and event processing
|
|
4
|
+
*
|
|
5
|
+
* Requirements covered:
|
|
6
|
+
* - 12.1: Webhook verification with hub.challenge response
|
|
7
|
+
* - 12.2: Incoming message event parsing (sender, text, attachments)
|
|
8
|
+
* - 12.3: Postback event parsing (title, payload)
|
|
9
|
+
* - 12.4: Opt-in event parsing (ref, user_ref)
|
|
10
|
+
* - 12.5: X-Hub-Signature-256 validation using HMAC SHA256
|
|
11
|
+
* - 12.6: Signature validation failure handling
|
|
12
|
+
* - 13.1: Comment event parsing (media_id, comment text, commenter info)
|
|
13
|
+
* - 13.2: Mention event parsing (media_id, mentioned by)
|
|
14
|
+
* - 13.3: Dual output system (Output 1: messaging, Output 2: content)
|
|
15
|
+
*/
|
|
16
|
+
export interface IVerifyQuery {
|
|
17
|
+
'hub.mode'?: string;
|
|
18
|
+
'hub.verify_token'?: string;
|
|
19
|
+
'hub.challenge'?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface IAttachment {
|
|
22
|
+
type: 'image' | 'audio' | 'video' | 'file';
|
|
23
|
+
payload: {
|
|
24
|
+
url: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface IMessageContent {
|
|
28
|
+
mid: string;
|
|
29
|
+
text?: string;
|
|
30
|
+
attachments?: IAttachment[];
|
|
31
|
+
}
|
|
32
|
+
export interface IPostbackContent {
|
|
33
|
+
mid: string;
|
|
34
|
+
title: string;
|
|
35
|
+
payload: string;
|
|
36
|
+
}
|
|
37
|
+
export interface IOptinContent {
|
|
38
|
+
ref: string;
|
|
39
|
+
user_ref?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface IMessagingEvent {
|
|
42
|
+
sender: {
|
|
43
|
+
id: string;
|
|
44
|
+
};
|
|
45
|
+
recipient: {
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
timestamp: number;
|
|
49
|
+
message?: IMessageContent;
|
|
50
|
+
postback?: IPostbackContent;
|
|
51
|
+
optin?: IOptinContent;
|
|
52
|
+
}
|
|
53
|
+
export interface IChangeEvent {
|
|
54
|
+
field: 'comments' | 'mentions';
|
|
55
|
+
value: {
|
|
56
|
+
media_id: string;
|
|
57
|
+
id: string;
|
|
58
|
+
text?: string;
|
|
59
|
+
from: {
|
|
60
|
+
id: string;
|
|
61
|
+
username: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export interface IWebhookEntry {
|
|
66
|
+
id: string;
|
|
67
|
+
time: number;
|
|
68
|
+
messaging?: IMessagingEvent[];
|
|
69
|
+
changes?: IChangeEvent[];
|
|
70
|
+
}
|
|
71
|
+
export interface IWebhookBody {
|
|
72
|
+
object: 'instagram';
|
|
73
|
+
entry: IWebhookEntry[];
|
|
74
|
+
}
|
|
75
|
+
export interface IWebhookResult {
|
|
76
|
+
messagingEvents: IMessagingEvent[];
|
|
77
|
+
contentEvents: IChangeEvent[];
|
|
78
|
+
}
|
|
79
|
+
export interface IParsedMessageEvent {
|
|
80
|
+
type: 'message';
|
|
81
|
+
senderId: string;
|
|
82
|
+
recipientId: string;
|
|
83
|
+
timestamp: number;
|
|
84
|
+
messageId: string;
|
|
85
|
+
text?: string;
|
|
86
|
+
attachments?: IAttachment[];
|
|
87
|
+
}
|
|
88
|
+
export interface IParsedPostbackEvent {
|
|
89
|
+
type: 'postback';
|
|
90
|
+
senderId: string;
|
|
91
|
+
recipientId: string;
|
|
92
|
+
timestamp: number;
|
|
93
|
+
messageId: string;
|
|
94
|
+
title: string;
|
|
95
|
+
payload: string;
|
|
96
|
+
}
|
|
97
|
+
export interface IParsedOptinEvent {
|
|
98
|
+
type: 'optin';
|
|
99
|
+
senderId: string;
|
|
100
|
+
recipientId: string;
|
|
101
|
+
timestamp: number;
|
|
102
|
+
ref: string;
|
|
103
|
+
userRef?: string;
|
|
104
|
+
}
|
|
105
|
+
export interface IParsedCommentEvent {
|
|
106
|
+
type: 'comment';
|
|
107
|
+
mediaId: string;
|
|
108
|
+
commentId: string;
|
|
109
|
+
text?: string;
|
|
110
|
+
from: {
|
|
111
|
+
id: string;
|
|
112
|
+
username: string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export interface IParsedMentionEvent {
|
|
116
|
+
type: 'mention';
|
|
117
|
+
mediaId: string;
|
|
118
|
+
mentionId: string;
|
|
119
|
+
from: {
|
|
120
|
+
id: string;
|
|
121
|
+
username: string;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export type ParsedMessagingEvent = IParsedMessageEvent | IParsedPostbackEvent | IParsedOptinEvent;
|
|
125
|
+
export type ParsedContentEvent = IParsedCommentEvent | IParsedMentionEvent;
|
|
126
|
+
export declare class WebhookHandler {
|
|
127
|
+
/**
|
|
128
|
+
* Verify webhook subscription request (Requirement 12.1)
|
|
129
|
+
* Returns challenge if verify token matches, null otherwise
|
|
130
|
+
*
|
|
131
|
+
* @param query - The query parameters from the verification request
|
|
132
|
+
* @param expectedVerifyToken - The expected verify token configured in the app
|
|
133
|
+
* @returns The challenge string if verification succeeds, null otherwise
|
|
134
|
+
*/
|
|
135
|
+
verifyWebhook(query: IVerifyQuery, expectedVerifyToken: string): string | null;
|
|
136
|
+
/**
|
|
137
|
+
* Validate webhook signature using HMAC SHA256 (Requirements 12.5, 12.6)
|
|
138
|
+
* Uses timing-safe comparison to prevent timing attacks
|
|
139
|
+
*
|
|
140
|
+
* @param payload - The raw request body as a string
|
|
141
|
+
* @param signature - The X-Hub-Signature-256 header value
|
|
142
|
+
* @param appSecret - The app secret used for HMAC calculation
|
|
143
|
+
* @returns true if signature is valid, false otherwise
|
|
144
|
+
*/
|
|
145
|
+
validateSignature(payload: string, signature: string, appSecret: string): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Process webhook payload and route events to appropriate outputs (Requirement 13.3)
|
|
148
|
+
* Output 1: Messaging events (messages, postbacks, optins)
|
|
149
|
+
* Output 2: Content events (comments, mentions)
|
|
150
|
+
*
|
|
151
|
+
* @param body - The webhook body from Instagram
|
|
152
|
+
* @returns Separated messaging and content events
|
|
153
|
+
*/
|
|
154
|
+
processWebhook(body: IWebhookBody): IWebhookResult;
|
|
155
|
+
/**
|
|
156
|
+
* Parse a messaging event into a more consumable format (Requirements 12.2, 12.3, 12.4)
|
|
157
|
+
* Extracts sender ID, message ID, text/attachments for messages
|
|
158
|
+
* Extracts sender ID, title, payload for postbacks
|
|
159
|
+
* Extracts ref, user_ref for optins
|
|
160
|
+
*
|
|
161
|
+
* @param event - The raw messaging event
|
|
162
|
+
* @returns Parsed event with extracted fields, or null if invalid
|
|
163
|
+
*/
|
|
164
|
+
parseMessagingEvent(event: IMessagingEvent): ParsedMessagingEvent | null;
|
|
165
|
+
/**
|
|
166
|
+
* Parse a content event into a more consumable format (Requirements 13.1, 13.2)
|
|
167
|
+
* Extracts media ID, comment ID, text, commenter info for comments
|
|
168
|
+
* Extracts media ID, mentioned by info for mentions
|
|
169
|
+
*
|
|
170
|
+
* @param event - The raw change event
|
|
171
|
+
* @returns Parsed event with extracted fields, or null if invalid
|
|
172
|
+
*/
|
|
173
|
+
parseContentEvent(event: IChangeEvent): ParsedContentEvent | null;
|
|
174
|
+
/**
|
|
175
|
+
* Process and parse all events from a webhook body
|
|
176
|
+
* Returns fully parsed events ready for n8n output
|
|
177
|
+
*
|
|
178
|
+
* @param body - The webhook body from Instagram
|
|
179
|
+
* @returns Parsed messaging and content events
|
|
180
|
+
*/
|
|
181
|
+
processAndParseWebhook(body: IWebhookBody): {
|
|
182
|
+
messagingEvents: ParsedMessagingEvent[];
|
|
183
|
+
contentEvents: ParsedContentEvent[];
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Check if a messaging event is a message type
|
|
187
|
+
*/
|
|
188
|
+
isMessageEvent(event: IMessagingEvent): boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Check if a messaging event is a postback type
|
|
191
|
+
*/
|
|
192
|
+
isPostbackEvent(event: IMessagingEvent): boolean;
|
|
193
|
+
/**
|
|
194
|
+
* Check if a messaging event is an opt-in type
|
|
195
|
+
*/
|
|
196
|
+
isOptinEvent(event: IMessagingEvent): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Check if a content event is a comment type
|
|
199
|
+
*/
|
|
200
|
+
isCommentEvent(event: IChangeEvent): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Check if a content event is a mention type
|
|
203
|
+
*/
|
|
204
|
+
isMentionEvent(event: IChangeEvent): boolean;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=WebhookHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebhookHandler.d.ts","sourceRoot":"","sources":["../../src/services/WebhookHandler.ts"],"names":[],"mappings":"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"}
|