Skip to content

Commit dca5284

Browse files
committed
feat: create meta module & set agency methods #27
1 parent 9ff5198 commit dca5284

File tree

5 files changed

+169
-16
lines changed

5 files changed

+169
-16
lines changed

src/ConnectorGlobal.ts

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,30 @@ export namespace ConnectorGlobal {
2626
// DAUM
2727
DAUM_API_KEY: string;
2828

29+
// FIGMA
30+
FIGMA_TEST_SECRET: string;
31+
FIGMA_TEST_FILE_KEY: string;
32+
2933
// GOOGLE
3034
GOOGLE_CLIENT_ID: string;
3135
GOOGLE_CLIENT_SECRET: string;
3236
GOOGLE_TEST_SECRET: string;
3337

38+
// IMWEB
39+
IMWEB_TEST_API_KEY: string;
40+
IMWEB_TEST_API_SECRET: string;
41+
42+
// KAKAO_TALK
43+
KAKAO_TALK_CLIENT_ID: string;
44+
KAKAO_TALK_TEST_REFRESH_TOKEN: string;
45+
46+
// META(facebook, instagram)
47+
META_CLIENT_ID: string;
48+
META_CLIENT_SECRET: string;
49+
META_PARENT_BUSINESS_ID: string;
50+
META_PARENT_BUSINESS_SYSTEM_USER_ACCESS_TOKEN: string;
51+
META_APP_ID: string;
52+
3453
// NAVER
3554
NAVER_CLIENT_ID: string;
3655
NAVER_CLIENT_SECRET: string;
@@ -41,30 +60,18 @@ export namespace ConnectorGlobal {
4160
// SERP
4261
SERP_API_KEY: string;
4362

63+
// SWEET_TRACKER
64+
TEST_SWEET_TRACKER_KEY: string;
65+
TEST_SWEET_TRACKER_T_INVOICE: string;
66+
4467
// TYPEFORM
4568
TYPEFORM_PERSONAL_ACCESS_KEY: string;
4669

47-
// FIGMA
48-
FIGMA_TEST_SECRET: string;
49-
FIGMA_TEST_FILE_KEY: string;
50-
5170
// ZOOM
5271
ZOOM_TEST_REFRESH_TOKEN: string;
5372
ZOOM_TEST_AUTHORIZATION_CODE: string;
5473
ZOOM_TEST_AUTHORIZATION_HEADER: string;
5574

56-
// SWEET_TRACKER
57-
TEST_SWEET_TRACKER_KEY: string;
58-
TEST_SWEET_TRACKER_T_INVOICE: string;
59-
60-
// KAKAO_TALK
61-
KAKAO_TALK_CLIENT_ID: string;
62-
KAKAO_TALK_TEST_REFRESH_TOKEN: string;
63-
64-
// IMWEB
65-
IMWEB_TEST_API_KEY: string;
66-
IMWEB_TEST_API_SECRET: string;
67-
6875
//----
6976
// INHOUSE SERVERS
7077
//----
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export namespace IMeta {
2+
export interface AccessTokenDto {
3+
access_token: string;
4+
token_type: "bearer";
5+
expires_in: number;
6+
}
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import core from "@nestia/core";
2+
import { Controller, Query } from "@nestjs/common";
3+
4+
import { IMeta } from "@wrtn/connector-api/lib/structures/connector/meta/IMeta";
5+
6+
import { ConnectorGlobal } from "../../../ConnectorGlobal";
7+
import { MetaProvider } from "../../../providers/connector/meta/MetaProvider";
8+
9+
@Controller("connector/meta")
10+
export class MetaController {
11+
/**
12+
* Meta login 인증창을 통해 유저가 로그인하면 콜백으로 호출되는 API.
13+
*
14+
* @internal
15+
*
16+
* @param query OAuth2 authorization code.
17+
* @returns access token DTO.
18+
*/
19+
@core.TypedRoute.Get("auth")
20+
async authorization(
21+
@Query() query: { code: string; state: string },
22+
): Promise<IMeta.AccessTokenDto> {
23+
const authorizationCode = query.code;
24+
25+
const res = await MetaProvider.getAccessToken({
26+
client_id: ConnectorGlobal.env.META_CLIENT_ID,
27+
client_secret: ConnectorGlobal.env.META_CLIENT_SECRET,
28+
code: authorizationCode,
29+
redirect_uri: "http://localhost:3003/connector/meta/auth",
30+
});
31+
32+
return res;
33+
}
34+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Module } from "@nestjs/common";
2+
3+
import { MetaController } from "./metaController";
4+
5+
@Module({
6+
controllers: [MetaController],
7+
})
8+
export class MetaModule {}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import axios from "axios";
2+
import { createHmac } from "crypto";
3+
4+
import { ConnectorGlobal } from "../../../ConnectorGlobal";
5+
6+
export namespace MetaProvider {
7+
export const baseUrl = "https://graph.facebook.com/v20.0/";
8+
9+
export async function getAccessToken(options: {
10+
client_id: string;
11+
client_secret: string;
12+
redirect_uri: string;
13+
code: string;
14+
}) {
15+
const queryParams = Object.entries(options)
16+
.map(([key, value]) => `${key}=${value}`)
17+
.join("&");
18+
19+
const res = await axios.get(
20+
`https://graph.facebook.com/oauth/access_token?${queryParams}`,
21+
);
22+
23+
return res.data;
24+
}
25+
26+
export async function setAgency(input: {
27+
businessId: string; // client business id
28+
userAccessToken: string; // user access token
29+
}) {
30+
const PARTNER_BM_ID = ConnectorGlobal.env.META_PARENT_BUSINESS_ID;
31+
const url = `${MetaProvider.baseUrl}/${PARTNER_BM_ID}/managed_businesses?existing_client_business_id=${input.businessId}&access_token=${input.userAccessToken}`;
32+
const res = await axios.post(url);
33+
return res.data;
34+
}
35+
36+
export async function getSystemUserAccessToken(input: {
37+
businessId: string; // client business id
38+
userAccessToken: string; // user access token
39+
}) {
40+
const pbmAcessToken = `${ConnectorGlobal.env.META_PARENT_BUSINESS_SYSTEM_USER_ACCESS_TOKEN}`;
41+
const appId = `${ConnectorGlobal.env.META_APP_ID}`;
42+
const url = `${MetaProvider.baseUrl}/${input.businessId}/access_token?scope=ads_management,pages_read_engagement&app_id=${appId}&access_token=${pbmAcessToken}`;
43+
const res = await axios.post(url);
44+
return res.data;
45+
}
46+
47+
/**
48+
*
49+
* @param input 조회할 대상의 액세스 토큰을 담은 요청 DTO.
50+
* @returns
51+
*/
52+
export async function getUserId(input: {
53+
/**
54+
* 조회할 대상의 액세스 토큰으로 타입이 유저가 아닌 모든 것을 포함한다.
55+
*/
56+
accessToken: string;
57+
}) {
58+
const url = `${baseUrl}/me?access_token=${input.accessToken}`;
59+
const res = await axios.get(url);
60+
return res.data;
61+
}
62+
63+
export async function assign(input: {
64+
/**
65+
* 할당할 자신의 아이디
66+
*/
67+
assetId: string;
68+
69+
/**
70+
* 할당받을 유저의 아이디 ( 여기서는 시스템 유저에 해당 )
71+
*/
72+
userId: string;
73+
74+
accessToken: string;
75+
}) {
76+
const url = `${baseUrl}/${input.assetId}/assigned_users?user=${input.userId}&tasks=MANAGE&access_token=${input.accessToken}`;
77+
const res = await axios.post(url);
78+
return res.data;
79+
}
80+
81+
export async function getPage(input: {
82+
/**
83+
* 권한을 가진 시스템 유저의 액세스 토큰
84+
*/
85+
systemUserAccessToken: string;
86+
}) {
87+
const url = `${MetaProvider.baseUrl}/me/accounts?access_token=${input.systemUserAccessToken}`;
88+
const res = await axios.get(url);
89+
return res.data;
90+
}
91+
92+
export function makeAppSecretProof(accessToken: string) {
93+
return createHmac("sha256", ConnectorGlobal.env.META_CLIENT_SECRET)
94+
.update(accessToken)
95+
.digest("hex");
96+
}
97+
}

0 commit comments

Comments
 (0)