@resultcrafter/aimanager-instagram-connector 0.1.2 → 0.2.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/aimanager/InstagramOAuth.js +155 -0
- package/index.js +81 -113
- package/package.json +1 -1
- package/template/configure.html +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
var crypto = require('crypto');
|
|
2
|
+
var axios = require('axios');
|
|
3
|
+
|
|
4
|
+
var INSTAGRAM_OAUTH_BASE = 'https://www.instagram.com/oauth';
|
|
5
|
+
var INSTAGRAM_API_BASE = 'https://api.instagram.com/oauth';
|
|
6
|
+
var INSTAGRAM_GRAPH_BASE = 'https://graph.instagram.com';
|
|
7
|
+
|
|
8
|
+
function getConfig() {
|
|
9
|
+
return {
|
|
10
|
+
appId: process.env.INSTAGRAM_APP_ID,
|
|
11
|
+
appSecret: process.env.INSTAGRAM_APP_SECRET,
|
|
12
|
+
redirectUri: process.env.INSTAGRAM_REDIRECT_URI,
|
|
13
|
+
stateSecret: process.env.OAUTH_STATE_SECRET || process.env.INSTAGRAM_APP_SECRET || 'default_secret',
|
|
14
|
+
scopes: ['instagram_business_basic', 'instagram_business_manage_messages'].join(',')
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function generateState(sessionId, projectId) {
|
|
19
|
+
var data = {
|
|
20
|
+
sessionId: sessionId || crypto.randomBytes(16).toString('hex'),
|
|
21
|
+
project_id: projectId,
|
|
22
|
+
timestamp: Date.now(),
|
|
23
|
+
nonce: crypto.randomBytes(8).toString('hex')
|
|
24
|
+
};
|
|
25
|
+
var cfg = getConfig();
|
|
26
|
+
var hmac = crypto.createHmac('sha256', cfg.stateSecret);
|
|
27
|
+
hmac.update(JSON.stringify(data));
|
|
28
|
+
var state = Buffer.from(JSON.stringify({
|
|
29
|
+
data: data,
|
|
30
|
+
signature: hmac.digest('hex')
|
|
31
|
+
})).toString('base64url');
|
|
32
|
+
return state;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function verifyState(state) {
|
|
36
|
+
try {
|
|
37
|
+
var cfg = getConfig();
|
|
38
|
+
var decoded = JSON.parse(Buffer.from(state, 'base64url').toString());
|
|
39
|
+
var hmac = crypto.createHmac('sha256', cfg.stateSecret);
|
|
40
|
+
hmac.update(JSON.stringify(decoded.data));
|
|
41
|
+
if (decoded.signature !== hmac.digest('hex')) return null;
|
|
42
|
+
if (Date.now() - decoded.data.timestamp > 600000) return null;
|
|
43
|
+
return decoded.data;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getAuthorizationUrl(projectId, token, appId) {
|
|
50
|
+
var cfg = getConfig();
|
|
51
|
+
if (!cfg.appId) throw new Error('INSTAGRAM_APP_ID not configured');
|
|
52
|
+
if (!cfg.redirectUri) throw new Error('INSTAGRAM_REDIRECT_URI not configured');
|
|
53
|
+
var state = generateState(null, projectId);
|
|
54
|
+
var params = new URLSearchParams({
|
|
55
|
+
client_id: cfg.appId,
|
|
56
|
+
redirect_uri: cfg.redirectUri,
|
|
57
|
+
scope: cfg.scopes,
|
|
58
|
+
response_type: 'code',
|
|
59
|
+
state: state
|
|
60
|
+
});
|
|
61
|
+
return { url: INSTAGRAM_OAUTH_BASE + '/authorize?' + params.toString(), state: state };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function exchangeCodeForToken(code) {
|
|
65
|
+
var cfg = getConfig();
|
|
66
|
+
if (!cfg.appId || !cfg.appSecret) throw new Error('Instagram app credentials not configured');
|
|
67
|
+
var tokenRes = await axios.post(INSTAGRAM_API_BASE + '/access_token',
|
|
68
|
+
new URLSearchParams({
|
|
69
|
+
client_id: cfg.appId,
|
|
70
|
+
client_secret: cfg.appSecret,
|
|
71
|
+
grant_type: 'authorization_code',
|
|
72
|
+
redirect_uri: cfg.redirectUri,
|
|
73
|
+
code: code
|
|
74
|
+
}),
|
|
75
|
+
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
|
|
76
|
+
);
|
|
77
|
+
var shortToken = tokenRes.data.access_token;
|
|
78
|
+
var userId = tokenRes.data.user_id;
|
|
79
|
+
var longRes = await axios.get(INSTAGRAM_GRAPH_BASE + '/access_token', {
|
|
80
|
+
params: { grant_type: 'ig_exchange_token', client_secret: cfg.appSecret, access_token: shortToken }
|
|
81
|
+
});
|
|
82
|
+
var longToken = longRes.data.access_token;
|
|
83
|
+
var userInfo = await getUserInfo(longToken);
|
|
84
|
+
return {
|
|
85
|
+
access_token: longToken,
|
|
86
|
+
token_type: longRes.data.token_type,
|
|
87
|
+
expires_in: longRes.data.expires_in,
|
|
88
|
+
user_id: userId,
|
|
89
|
+
instagram_business_id: userInfo.instagram_business_id || userInfo.user_id || userId,
|
|
90
|
+
username: userInfo.username,
|
|
91
|
+
user_info: userInfo,
|
|
92
|
+
created_at: Date.now(),
|
|
93
|
+
expires_at: Date.now() + (longRes.data.expires_in * 1000)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function refreshToken(currentToken) {
|
|
98
|
+
var cfg = getConfig();
|
|
99
|
+
var res = await axios.get(INSTAGRAM_GRAPH_BASE + '/refresh_access_token', {
|
|
100
|
+
params: { grant_type: 'ig_refresh_token', access_token: currentToken }
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
access_token: res.data.access_token,
|
|
104
|
+
token_type: res.data.token_type,
|
|
105
|
+
expires_in: res.data.expires_in,
|
|
106
|
+
created_at: Date.now(),
|
|
107
|
+
expires_at: Date.now() + (res.data.expires_in * 1000)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function getUserInfo(accessToken) {
|
|
112
|
+
try {
|
|
113
|
+
var res = await axios.get(INSTAGRAM_GRAPH_BASE + '/v23.0/me', {
|
|
114
|
+
params: {
|
|
115
|
+
fields: 'id,user_id,username,name,account_type,media_count,followers_count,follows_count,profile_picture_url',
|
|
116
|
+
access_token: accessToken
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
app_scoped_id: res.data.id,
|
|
121
|
+
instagram_business_id: res.data.user_id,
|
|
122
|
+
username: res.data.username,
|
|
123
|
+
name: res.data.name,
|
|
124
|
+
account_type: res.data.account_type,
|
|
125
|
+
profile_picture_url: res.data.profile_picture_url
|
|
126
|
+
};
|
|
127
|
+
} catch (e) {
|
|
128
|
+
return { username: null };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function getTokenStatus(db, projectId) {
|
|
133
|
+
var settings = await db.get('instagram-' + projectId);
|
|
134
|
+
if (!settings || !settings.ig_oauth_token) {
|
|
135
|
+
return { connected: false, instagram_username: null };
|
|
136
|
+
}
|
|
137
|
+
var token = settings.ig_oauth_token;
|
|
138
|
+
var daysRemaining = token.expires_at ? Math.floor((token.expires_at - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
139
|
+
return {
|
|
140
|
+
connected: true,
|
|
141
|
+
instagram_username: token.username || settings.instagram_username || 'Instagram Account',
|
|
142
|
+
user_id: token.instagram_business_id || token.user_id,
|
|
143
|
+
days_remaining: daysRemaining,
|
|
144
|
+
needs_refresh: daysRemaining !== null ? daysRemaining < 7 : false
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
getAuthorizationUrl: getAuthorizationUrl,
|
|
150
|
+
exchangeCodeForToken: exchangeCodeForToken,
|
|
151
|
+
refreshToken: refreshToken,
|
|
152
|
+
getUserInfo: getUserInfo,
|
|
153
|
+
verifyState: verifyState,
|
|
154
|
+
getTokenStatus: getTokenStatus
|
|
155
|
+
};
|
package/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const { TiledeskInstagramTranslator } = require('./aimanager/TiledeskInstagramTr
|
|
|
16
16
|
const { TiledeskSubscriptionClient } = require('./aimanager/TiledeskSubscriptionClient');
|
|
17
17
|
const { FacebookClient } = require('./aimanager/FacebookClient');
|
|
18
18
|
const { MessageHandler } = require('./aimanager/MessageHandler');
|
|
19
|
+
const instagramOAuth = require('./aimanager/InstagramOAuth');
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
// mongo
|
|
@@ -110,13 +111,14 @@ router.post('/install', async (req, res) => {
|
|
|
110
111
|
query: {
|
|
111
112
|
"project_id": project_id,
|
|
112
113
|
"app_id": app_id,
|
|
113
|
-
"token": token
|
|
114
|
+
"token": token,
|
|
115
|
+
"oauth_url": BASE_URL + '/auth/instagram/start'
|
|
114
116
|
}
|
|
115
117
|
}));
|
|
116
118
|
|
|
117
119
|
}).catch((err) => {
|
|
118
|
-
winston.error("(
|
|
119
|
-
winston.error("(
|
|
120
|
+
winston.error("(ig) installation error: ", err.data)
|
|
121
|
+
winston.error("(ig) installation error: " + err.data)
|
|
120
122
|
res.send("An error occurred during the installation");
|
|
121
123
|
})
|
|
122
124
|
|
|
@@ -184,6 +186,8 @@ router.get('/configure', async (req, res) => {
|
|
|
184
186
|
return []
|
|
185
187
|
})
|
|
186
188
|
|
|
189
|
+
var tokenStatus = await instagramOAuth.getTokenStatus(db, project_id);
|
|
190
|
+
|
|
187
191
|
if (settings) {
|
|
188
192
|
winston.debug("(fbm) settings found: ", settings);
|
|
189
193
|
|
|
@@ -192,7 +196,7 @@ router.get('/configure', async (req, res) => {
|
|
|
192
196
|
var replacements = {
|
|
193
197
|
app_version: pjson.version,
|
|
194
198
|
project_id: project_id,
|
|
195
|
-
ig_token:
|
|
199
|
+
ig_token: tokenStatus.connected ? tokenStatus.instagram_username : null,
|
|
196
200
|
token: token,
|
|
197
201
|
app_id: app_id,
|
|
198
202
|
endpoint: BASE_URL,
|
|
@@ -201,9 +205,10 @@ router.get('/configure', async (req, res) => {
|
|
|
201
205
|
department_id: settings.department_id,
|
|
202
206
|
departments: departments,
|
|
203
207
|
brand_name: BRAND_NAME,
|
|
204
|
-
instagram_username:
|
|
208
|
+
instagram_username: tokenStatus.instagram_username || 'Instagram Account',
|
|
205
209
|
show_info_message: settings.show_info_message !== undefined ? settings.show_info_message : true,
|
|
206
|
-
subscription_id: settings.subscription_id
|
|
210
|
+
subscription_id: settings.subscription_id,
|
|
211
|
+
oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
|
|
207
212
|
}
|
|
208
213
|
var html = template(replacements)
|
|
209
214
|
res.send(html);
|
|
@@ -221,7 +226,8 @@ router.get('/configure', async (req, res) => {
|
|
|
221
226
|
app_id: app_id,
|
|
222
227
|
endpoint: BASE_URL,
|
|
223
228
|
departments: departments,
|
|
224
|
-
brand_name: BRAND_NAME
|
|
229
|
+
brand_name: BRAND_NAME,
|
|
230
|
+
oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
|
|
225
231
|
}
|
|
226
232
|
var html = template(replacements);
|
|
227
233
|
res.send(html);
|
|
@@ -232,7 +238,7 @@ router.get('/configure', async (req, res) => {
|
|
|
232
238
|
})
|
|
233
239
|
|
|
234
240
|
router.post('/update', async (req, res) => {
|
|
235
|
-
winston.verbose("(
|
|
241
|
+
winston.verbose("(ig) /update");
|
|
236
242
|
|
|
237
243
|
let project_id = req.body.project_id;
|
|
238
244
|
let token = req.body.token;
|
|
@@ -247,10 +253,11 @@ router.post('/update', async (req, res) => {
|
|
|
247
253
|
await db.set(CONTENT_KEY, settings);
|
|
248
254
|
}
|
|
249
255
|
|
|
250
|
-
|
|
256
|
+
var tokenStatus = await instagramOAuth.getTokenStatus(db, project_id);
|
|
257
|
+
|
|
251
258
|
const tdChannel = new TiledeskChannel({ settings: { project_id: project_id, token: token }, API_URL: API_URL });
|
|
252
259
|
let departments = await tdChannel.getDepartments(token);
|
|
253
|
-
winston.verbose("(
|
|
260
|
+
winston.verbose("(ig) found " + departments.length + " departments")
|
|
254
261
|
|
|
255
262
|
readHTMLFile('/configure.html', (err, html) => {
|
|
256
263
|
|
|
@@ -258,7 +265,7 @@ router.post('/update', async (req, res) => {
|
|
|
258
265
|
var replacements = {
|
|
259
266
|
app_version: pjson.version,
|
|
260
267
|
project_id: project_id,
|
|
261
|
-
ig_token:
|
|
268
|
+
ig_token: tokenStatus.connected ? tokenStatus.instagram_username : null,
|
|
262
269
|
token: token,
|
|
263
270
|
app_id: app_id,
|
|
264
271
|
endpoint: BASE_URL,
|
|
@@ -268,8 +275,9 @@ router.post('/update', async (req, res) => {
|
|
|
268
275
|
departments: departments,
|
|
269
276
|
brand_name: BRAND_NAME,
|
|
270
277
|
show_info_message: settings ? (settings.show_info_message !== undefined ? settings.show_info_message : true) : true,
|
|
271
|
-
instagram_username:
|
|
272
|
-
subscription_id: settings ? settings.subscription_id : null
|
|
278
|
+
instagram_username: tokenStatus.instagram_username || 'Instagram Account',
|
|
279
|
+
subscription_id: settings ? settings.subscription_id : null,
|
|
280
|
+
oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token + '&app_id=' + app_id
|
|
273
281
|
}
|
|
274
282
|
var html = template(replacements)
|
|
275
283
|
res.send(html);
|
|
@@ -278,7 +286,7 @@ router.post('/update', async (req, res) => {
|
|
|
278
286
|
})
|
|
279
287
|
|
|
280
288
|
router.post('/update_advanced', async (req, res) => {
|
|
281
|
-
winston.verbose("(
|
|
289
|
+
winston.verbose("(ig) /update_advanced");
|
|
282
290
|
|
|
283
291
|
let project_id = req.body.project_id;
|
|
284
292
|
let show_info_message = req.body.show_info_message === 'on';
|
|
@@ -291,6 +299,8 @@ router.post('/update_advanced', async (req, res) => {
|
|
|
291
299
|
await db.set(CONTENT_KEY, settings);
|
|
292
300
|
}
|
|
293
301
|
|
|
302
|
+
var tokenStatus = await instagramOAuth.getTokenStatus(db, project_id);
|
|
303
|
+
|
|
294
304
|
const tdChannel = new TiledeskChannel({ settings: { project_id: project_id, token: req.body.token }, API_URL: API_URL });
|
|
295
305
|
let departments = await tdChannel.getDepartments(req.body.token).catch(() => []);
|
|
296
306
|
|
|
@@ -299,7 +309,7 @@ router.post('/update_advanced', async (req, res) => {
|
|
|
299
309
|
var replacements = {
|
|
300
310
|
app_version: pjson.version,
|
|
301
311
|
project_id: project_id,
|
|
302
|
-
ig_token:
|
|
312
|
+
ig_token: tokenStatus.connected ? tokenStatus.instagram_username : null,
|
|
303
313
|
token: req.body.token,
|
|
304
314
|
endpoint: BASE_URL,
|
|
305
315
|
pages: settings ? settings.pages : null,
|
|
@@ -308,8 +318,9 @@ router.post('/update_advanced', async (req, res) => {
|
|
|
308
318
|
departments: departments,
|
|
309
319
|
brand_name: BRAND_NAME,
|
|
310
320
|
show_info_message: show_info_message,
|
|
311
|
-
instagram_username:
|
|
312
|
-
subscription_id: settings ? settings.subscription_id : null
|
|
321
|
+
instagram_username: tokenStatus.instagram_username || 'Instagram Account',
|
|
322
|
+
subscription_id: settings ? settings.subscription_id : null,
|
|
323
|
+
oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + req.body.token
|
|
313
324
|
}
|
|
314
325
|
var html = template(replacements)
|
|
315
326
|
res.send(html);
|
|
@@ -632,93 +643,63 @@ router.get('/webhookFB', async (req, res) => {
|
|
|
632
643
|
return res.sendStatus(403);
|
|
633
644
|
})
|
|
634
645
|
|
|
635
|
-
router.get('/
|
|
636
|
-
winston.verbose("(
|
|
637
|
-
|
|
638
|
-
let project_id = JSON.parse(req.query.state).project_id;
|
|
639
|
-
var code = req.query.code;
|
|
640
|
-
let token = JSON.parse(req.query.state).token;
|
|
641
|
-
let app_id = JSON.parse(req.query.state).app_id;
|
|
646
|
+
router.get('/auth/instagram/start', async (req, res) => {
|
|
647
|
+
winston.verbose("(ig) /auth/instagram/start");
|
|
642
648
|
|
|
643
|
-
|
|
644
|
-
|
|
649
|
+
let project_id = req.query.project_id;
|
|
650
|
+
let token = req.query.token;
|
|
651
|
+
let app_id = req.query.app_id;
|
|
645
652
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
event: 'message.create.request.channel.instagram',
|
|
653
|
+
if (!project_id) {
|
|
654
|
+
return res.status(400).send('Missing project_id parameter');
|
|
649
655
|
}
|
|
650
656
|
|
|
651
|
-
let subscription, access_token, pages_list;
|
|
652
657
|
try {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
658
|
+
var auth = instagramOAuth.getAuthorizationUrl(project_id, token, app_id);
|
|
659
|
+
winston.info("(ig) Redirecting to Instagram OAuth for project " + project_id);
|
|
660
|
+
res.redirect(auth.url);
|
|
656
661
|
} catch (err) {
|
|
657
|
-
winston.error("(
|
|
658
|
-
|
|
662
|
+
winston.error("(ig) OAuth start error: ", err.message);
|
|
663
|
+
res.status(500).send('OAuth configuration error: ' + err.message);
|
|
659
664
|
}
|
|
665
|
+
});
|
|
660
666
|
|
|
661
|
-
|
|
662
|
-
winston.
|
|
663
|
-
winston.debug("(fbm) pages_list: " + pages_list);
|
|
664
|
-
|
|
665
|
-
let CONTENT_KEY = "instagram-" + project_id;
|
|
667
|
+
router.get('/oauth-callback', async (req, res) => {
|
|
668
|
+
winston.verbose("(ig) /oauth-callback");
|
|
666
669
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
access_token: single_page.access_token,
|
|
673
|
-
category: single_page.category,
|
|
674
|
-
active: false
|
|
675
|
-
}
|
|
676
|
-
pages.push(page);
|
|
677
|
-
try {
|
|
678
|
-
let event_sub = await fbClient.messageEventSubscription(single_page.id, single_page.access_token);
|
|
679
|
-
winston.debug("(fbm) event subscription: " + event_sub?.status + " " + event_sub?.statusText)
|
|
680
|
-
} catch (err) {
|
|
681
|
-
winston.error("(fbm) event subscription error: ", err?.response?.data || err.message || err);
|
|
682
|
-
}
|
|
683
|
-
})
|
|
684
|
-
|
|
685
|
-
let settings = {
|
|
686
|
-
project_id: project_id,
|
|
687
|
-
token: token,
|
|
688
|
-
access_token: access_token,
|
|
689
|
-
subscription_id: subscription._id,
|
|
690
|
-
secret: subscription.secret,
|
|
691
|
-
pages: pages,
|
|
692
|
-
app_id: app_id
|
|
670
|
+
var code = req.query.code;
|
|
671
|
+
var stateStr = req.query.state;
|
|
672
|
+
if (!code || !stateStr) {
|
|
673
|
+
winston.error("(ig) Missing code or state parameter");
|
|
674
|
+
return res.status(400).send("Missing authorization code or state");
|
|
693
675
|
}
|
|
694
676
|
|
|
695
|
-
|
|
677
|
+
var state = instagramOAuth.verifyState(stateStr);
|
|
678
|
+
if (!state) {
|
|
679
|
+
winston.error("(ig) Invalid or expired state");
|
|
680
|
+
return res.status(400).send("Invalid or expired state parameter. Please try connecting again.");
|
|
681
|
+
}
|
|
696
682
|
|
|
697
|
-
|
|
698
|
-
//var redirect_uri = DASHBOARD_BASE_URL + "/#/project/" + project_id + "/app-store-install/" + app_id + "/run";
|
|
699
|
-
var redirect_uri = DASHBOARD_BASE_URL + "/#/project/" + project_id + "/integrations?name=instagram";
|
|
700
|
-
console.log("(fbm) redirect_uri: ", redirect_uri);
|
|
701
|
-
res.redirect(redirect_uri);
|
|
683
|
+
var projectId = state.project_id;
|
|
702
684
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
secret: settings.secret,
|
|
715
|
-
pages: settings.pages
|
|
716
|
-
}
|
|
717
|
-
var html = template(replacements)
|
|
718
|
-
res.send(html);
|
|
719
|
-
})
|
|
720
|
-
*/
|
|
685
|
+
try {
|
|
686
|
+
var tokenData = await instagramOAuth.exchangeCodeForToken(code);
|
|
687
|
+
winston.info("(ig) Token obtained for project " + projectId + ", user: " + (tokenData.username || 'unknown'));
|
|
688
|
+
|
|
689
|
+
var CONTENT_KEY = "instagram-" + projectId;
|
|
690
|
+
var settings = await db.get(CONTENT_KEY) || {};
|
|
691
|
+
settings.ig_oauth_token = tokenData;
|
|
692
|
+
settings.instagram_username = tokenData.username;
|
|
693
|
+
settings.instagram_business_id = tokenData.instagram_business_id;
|
|
694
|
+
settings.connected = true;
|
|
695
|
+
await db.set(CONTENT_KEY, settings);
|
|
721
696
|
|
|
697
|
+
var redirect_uri = DASHBOARD_BASE_URL + "/#/project/" + projectId + "/integrations?name=instagram";
|
|
698
|
+
res.redirect(redirect_uri);
|
|
699
|
+
} catch (err) {
|
|
700
|
+
winston.error("(ig) OAuth callback error: ", err?.response?.data || err.message || err);
|
|
701
|
+
res.status(500).send("Authentication failed: " + (err.message || 'Unknown error'));
|
|
702
|
+
}
|
|
722
703
|
})
|
|
723
704
|
|
|
724
705
|
router.post('/enablePage', async (req, res) => {
|
|
@@ -858,38 +839,25 @@ router.post('/disablePage', async (req, res) => {
|
|
|
858
839
|
})
|
|
859
840
|
|
|
860
841
|
router.post('/disconnect', async (req, res) => {
|
|
861
|
-
winston.verbose("(
|
|
842
|
+
winston.verbose("(ig) /disconnect");
|
|
862
843
|
|
|
863
844
|
let project_id = req.body.project_id;
|
|
864
845
|
let token = req.body.token;
|
|
865
|
-
let subscription_id = req.body.subscription_id;
|
|
866
846
|
|
|
867
847
|
let CONTENT_KEY = "instagram-" + project_id;
|
|
868
|
-
|
|
869
848
|
let settings = await db.get(CONTENT_KEY);
|
|
870
849
|
|
|
871
|
-
if (settings
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
await db.remove(CONTENT_KEY)
|
|
881
|
-
winston.verbose("(fbm) Content deleted.");
|
|
882
|
-
|
|
883
|
-
const tdClient = new TiledeskSubscriptionClient({ API_URL: API_URL, project_id: project_id, token: token })
|
|
884
|
-
if (settings && settings.subscription_id) {
|
|
885
|
-
tdClient.unsubscribe(settings.subscription_id).catch((err) => {
|
|
886
|
-
winston.error("(fbm) unsubscribe error: ", err);
|
|
887
|
-
});
|
|
850
|
+
if (settings) {
|
|
851
|
+
settings.ig_oauth_token = null;
|
|
852
|
+
settings.instagram_username = null;
|
|
853
|
+
settings.instagram_business_id = null;
|
|
854
|
+
settings.connected = false;
|
|
855
|
+
await db.set(CONTENT_KEY, settings);
|
|
856
|
+
winston.info("(ig) OAuth token cleared for project " + project_id);
|
|
888
857
|
}
|
|
889
858
|
|
|
890
859
|
const tdChannel = new TiledeskChannel({ settings: { project_id: project_id, token: token }, API_URL: API_URL })
|
|
891
860
|
let departments = await tdChannel.getDepartments(token).catch(() => []);
|
|
892
|
-
winston.debug("(fbm) found " + departments.length + " departments")
|
|
893
861
|
|
|
894
862
|
readHTMLFile('/configure.html', (err, html) => {
|
|
895
863
|
var template = handlebars.compile(html);
|
|
@@ -900,12 +868,12 @@ router.post('/disconnect', async (req, res) => {
|
|
|
900
868
|
token: token,
|
|
901
869
|
endpoint: BASE_URL,
|
|
902
870
|
departments: departments,
|
|
903
|
-
brand_name: BRAND_NAME
|
|
871
|
+
brand_name: BRAND_NAME,
|
|
872
|
+
oauth_url: BASE_URL + '/auth/instagram/start?project_id=' + project_id + '&token=' + token
|
|
904
873
|
}
|
|
905
874
|
var html = template(replacements)
|
|
906
875
|
return res.send(html);
|
|
907
876
|
})
|
|
908
|
-
|
|
909
877
|
})
|
|
910
878
|
|
|
911
879
|
function startApp(settings, callback) {
|
package/package.json
CHANGED
package/template/configure.html
CHANGED
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
var height = 700;
|
|
248
248
|
var left = (screen.width - width) / 2;
|
|
249
249
|
var top = (screen.height - height) / 2;
|
|
250
|
-
var oauthUrl = '
|
|
250
|
+
var oauthUrl = '{{ oauth_url }}';
|
|
251
251
|
var popup = window.open(oauthUrl, 'instagram-oauth', 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top);
|
|
252
252
|
|
|
253
253
|
var timer = setInterval(function() {
|