Skip to content

Commit fbd977a

Browse files
authored
Merge pull request #375 from gitroomhq/feat/refresh
hotfix: fix refresh mechanism
2 parents 581953b + b6da5b8 commit fbd977a

19 files changed

+154
-113
lines changed

apps/backend/src/api/routes/integrations.controller.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { NotEnoughScopesFilter } from '@gitroom/nestjs-libraries/integrations/in
2828
import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service';
2929
import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto';
3030
import { AuthService } from '@gitroom/helpers/auth/auth.service';
31+
import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
3132

3233
@ApiTags('Integrations')
3334
@Controller('/integrations')
@@ -156,7 +157,12 @@ export class IntegrationsController {
156157
: undefined;
157158

158159
const { codeVerifier, state, url } =
159-
await integrationProvider.generateAuthUrl(refresh, getExternalUrl);
160+
await integrationProvider.generateAuthUrl(getExternalUrl);
161+
162+
if (refresh) {
163+
await ioRedis.set(`refresh:${state}`, refresh, 'EX', 300);
164+
}
165+
160166
await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 300);
161167
await ioRedis.set(
162168
`external:${state}`,
@@ -311,6 +317,11 @@ export class IntegrationsController {
311317
await ioRedis.del(`external:${body.state}`);
312318
}
313319

320+
const refresh = await ioRedis.get(`refresh:${body.state}`);
321+
if (refresh) {
322+
await ioRedis.del(`refresh:${body.state}`);
323+
}
324+
314325
const {
315326
accessToken,
316327
expiresIn,
@@ -319,14 +330,28 @@ export class IntegrationsController {
319330
name,
320331
picture,
321332
username,
322-
} = await integrationProvider.authenticate(
323-
{
324-
code: body.code,
325-
codeVerifier: getCodeVerifier,
326-
refresh: body.refresh,
327-
},
328-
details ? JSON.parse(details) : undefined
329-
);
333+
// eslint-disable-next-line no-async-promise-executor
334+
} = await new Promise<AuthTokenDetails>(async (res) => {
335+
const auth = await integrationProvider.authenticate(
336+
{
337+
code: body.code,
338+
codeVerifier: getCodeVerifier,
339+
refresh: body.refresh,
340+
},
341+
details ? JSON.parse(details) : undefined
342+
);
343+
344+
if (refresh && integrationProvider.reConnect) {
345+
const newAuth = await integrationProvider.reConnect(
346+
auth.id,
347+
refresh,
348+
auth.accessToken
349+
);
350+
return res(newAuth);
351+
}
352+
353+
return res(auth);
354+
});
330355

331356
if (!id) {
332357
throw new Error('Invalid api key');
@@ -343,7 +368,7 @@ export class IntegrationsController {
343368
refreshToken,
344369
expiresIn,
345370
username,
346-
integrationProvider.isBetweenSteps,
371+
refresh ? false : integrationProvider.isBetweenSteps,
347372
body.refresh,
348373
+body.timezone,
349374
details

apps/frontend/src/components/launches/launches.component.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,13 @@ export const LaunchesComponent = () => {
126126
{sortedIntegrations.map((integration) => (
127127
<div
128128
{...(integration.refreshNeeded && {
129+
onClick: refreshChannel(integration),
129130
'data-tooltip-id': 'tooltip',
130131
'data-tooltip-content':
131132
'Channel disconnected, click to reconnect.',
132133
})}
133134
key={integration.id}
134-
className="flex gap-[8px] items-center"
135+
className={clsx("flex gap-[8px] items-center", integration.refreshNeeded && 'cursor-pointer')}
135136
>
136137
<div
137138
className={clsx(

apps/frontend/src/components/layout/continue.provider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { FC, useCallback, useEffect, useMemo } from 'react';
1+
import React, { FC, useCallback, useMemo } from 'react';
22
import { useRouter, useSearchParams } from 'next/navigation';
33
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
44
import { continueProviderList } from '@gitroom/frontend/components/launches/providers/continue-provider/list';

libraries/nestjs-libraries/src/integrations/social.abstract.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export abstract class SocialAbstract {
2626
let json = '{}';
2727
try {
2828
json = await request.text();
29+
console.log(json);
2930
} catch (err) {
3031
json = '{}';
3132
}

libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider {
5454
};
5555
}
5656

57-
async generateAuthUrl(refresh?: string) {
57+
async generateAuthUrl() {
5858
const state = makeId(6);
5959
return {
6060
url: '',

libraries/nestjs-libraries/src/integrations/social/discord.provider.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,13 @@ export class DiscordProvider extends SocialAbstract implements SocialProvider {
4949
username: '',
5050
};
5151
}
52-
async generateAuthUrl(refresh?: string) {
52+
async generateAuthUrl() {
5353
const state = makeId(6);
5454
return {
5555
url: `https://discord.com/oauth2/authorize?client_id=${
5656
process.env.DISCORD_CLIENT_ID
5757
}&permissions=377957124096&response_type=code&redirect_uri=${encodeURIComponent(
58-
`${process.env.FRONTEND_URL}/integrations/social/discord${
59-
refresh ? `?refresh=${refresh}` : ''
60-
}`
58+
`${process.env.FRONTEND_URL}/integrations/social/discord`
6159
)}&integration_type=0&scope=bot+identify+guilds&state=${state}`,
6260
codeVerifier: makeId(10),
6361
state,

libraries/nestjs-libraries/src/integrations/social/dribbble.provider.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,13 @@ export class DribbbleProvider extends SocialAbstract implements SocialProvider {
7575
);
7676
}
7777

78-
async generateAuthUrl(refresh?: string) {
78+
async generateAuthUrl() {
7979
const state = makeId(6);
8080
return {
8181
url: `https://dribbble.com/oauth/authorize?client_id=${
8282
process.env.DRIBBBLE_CLIENT_ID
8383
}&redirect_uri=${encodeURIComponent(
84-
`${process.env.FRONTEND_URL}/integrations/social/dribbble${
85-
refresh ? `?refresh=${refresh}` : ''
86-
}`
84+
`${process.env.FRONTEND_URL}/integrations/social/dribbble`
8785
)}&response_type=code&scope=${this.scopes.join('+')}&state=${state}`,
8886
codeVerifier: makeId(10),
8987
state,

libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
99
import dayjs from 'dayjs';
1010
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
11+
import { string } from 'yup';
1112

1213
export class FacebookProvider extends SocialAbstract implements SocialProvider {
1314
identifier = 'facebook';
@@ -33,16 +34,14 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider {
3334
};
3435
}
3536

36-
async generateAuthUrl(refresh?: string) {
37+
async generateAuthUrl() {
3738
const state = makeId(6);
3839
return {
3940
url:
4041
'https://www.facebook.com/v20.0/dialog/oauth' +
4142
`?client_id=${process.env.FACEBOOK_APP_ID}` +
4243
`&redirect_uri=${encodeURIComponent(
43-
`${process.env.FRONTEND_URL}/integrations/social/facebook${
44-
refresh ? `?refresh=${refresh}` : ''
45-
}`
44+
`${process.env.FRONTEND_URL}/integrations/social/facebook`
4645
)}` +
4746
`&state=${state}` +
4847
`&scope=${this.scopes.join(',')}`,
@@ -51,6 +50,27 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider {
5150
};
5251
}
5352

53+
async reConnect(
54+
id: string,
55+
requiredId: string,
56+
accessToken: string
57+
): Promise<AuthTokenDetails> {
58+
const information = await this.fetchPageInformation(
59+
accessToken,
60+
requiredId
61+
);
62+
63+
return {
64+
id: information.id,
65+
name: information.name,
66+
accessToken: information.access_token,
67+
refreshToken: information.access_token,
68+
expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(),
69+
picture: information.picture,
70+
username: information.username,
71+
};
72+
}
73+
5474
async authenticate(params: {
5575
code: string;
5676
codeVerifier: string;
@@ -91,22 +111,6 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider {
91111
.map((p: any) => p.permission);
92112
this.checkScopes(this.scopes, permissions);
93113

94-
if (params.refresh) {
95-
const information = await this.fetchPageInformation(
96-
access_token,
97-
params.refresh
98-
);
99-
return {
100-
id: information.id,
101-
name: information.name,
102-
accessToken: information.access_token,
103-
refreshToken: information.access_token,
104-
expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(),
105-
picture: information.picture,
106-
username: information.username,
107-
};
108-
}
109-
110114
const {
111115
id,
112116
name,
@@ -174,7 +178,11 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider {
174178
let finalId = '';
175179
let finalUrl = '';
176180
if ((firstPost?.media?.[0]?.url?.indexOf('mp4') || -2) > -1) {
177-
const { id: videoId, permalink_url, ...all } = await (
181+
const {
182+
id: videoId,
183+
permalink_url,
184+
...all
185+
} = await (
178186
await this.fetch(
179187
`https://graph.facebook.com/v20.0/${id}/videos?access_token=${accessToken}&fields=id,permalink_url`,
180188
{

libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
99
import { timer } from '@gitroom/helpers/utils/timer';
1010
import dayjs from 'dayjs';
1111
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
12+
import { string } from 'yup';
1213

1314
export class InstagramProvider
1415
extends SocialAbstract
@@ -39,16 +40,39 @@ export class InstagramProvider
3940
};
4041
}
4142

42-
async generateAuthUrl(refresh?: string) {
43+
async reConnect(
44+
id: string,
45+
requiredId: string,
46+
accessToken: string
47+
): Promise<AuthTokenDetails> {
48+
const findPage = (await this.pages(accessToken)).find(
49+
(p) => p.id === requiredId
50+
);
51+
52+
const information = await this.fetchPageInformation(accessToken, {
53+
id: requiredId,
54+
pageId: findPage?.pageId!,
55+
});
56+
57+
return {
58+
id: information.id,
59+
name: information.name,
60+
accessToken: information.access_token,
61+
refreshToken: information.access_token,
62+
expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(),
63+
picture: information.picture,
64+
username: information.username,
65+
};
66+
}
67+
68+
async generateAuthUrl() {
4369
const state = makeId(6);
4470
return {
4571
url:
4672
'https://www.facebook.com/v20.0/dialog/oauth' +
4773
`?client_id=${process.env.FACEBOOK_APP_ID}` +
4874
`&redirect_uri=${encodeURIComponent(
49-
`${process.env.FRONTEND_URL}/integrations/social/instagram${
50-
refresh ? `?refresh=${refresh}` : ''
51-
}`
75+
`${process.env.FRONTEND_URL}/integrations/social/instagram`
5276
)}` +
5377
`&state=${state}` +
5478
`&scope=${encodeURIComponent(this.scopes.join(','))}`,
@@ -109,26 +133,6 @@ export class InstagramProvider
109133
)
110134
).json();
111135

112-
if (params.refresh) {
113-
const findPage = (await this.pages(access_token)).find(
114-
(p) => p.id === params.refresh
115-
);
116-
const information = await this.fetchPageInformation(access_token, {
117-
id: params.refresh,
118-
pageId: findPage?.pageId!,
119-
});
120-
121-
return {
122-
id: information.id,
123-
name: information.name,
124-
accessToken: information.access_token,
125-
refreshToken: information.access_token,
126-
expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(),
127-
picture: information.picture,
128-
username: information.username,
129-
};
130-
}
131-
132136
return {
133137
id,
134138
name,
@@ -187,6 +191,7 @@ export class InstagramProvider
187191
)
188192
).json();
189193

194+
console.log(id, name, profile_picture_url, username);
190195
return {
191196
id,
192197
name,
@@ -206,7 +211,9 @@ export class InstagramProvider
206211
const medias = await Promise.all(
207212
firstPost?.media?.map(async (m) => {
208213
const caption =
209-
firstPost.media?.length === 1 ? `&caption=${encodeURIComponent(firstPost.message)}` : ``;
214+
firstPost.media?.length === 1
215+
? `&caption=${encodeURIComponent(firstPost.message)}`
216+
: ``;
210217
const isCarousel =
211218
(firstPost?.media?.length || 0) > 1 ? `&is_carousel_item=true` : ``;
212219
const mediaType =

0 commit comments

Comments
 (0)