Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions src/ConnectorGlobal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,31 @@ export namespace ConnectorGlobal {
// DAUM
DAUM_API_KEY: string;

// FIGMA
FIGMA_TEST_SECRET: string;
FIGMA_TEST_FILE_KEY: string;

// GOOGLE
GOOGLE_CLIENT_ID: string;
GOOGLE_CLIENT_SECRET: string;
GOOGLE_TEST_SECRET: string;

// IMWEB
IMWEB_TEST_API_KEY: string;
IMWEB_TEST_API_SECRET: string;

// KAKAO_TALK
KAKAO_TALK_CLIENT_ID: string;
KAKAO_TALK_CLIENT_SECRET: string;
KAKAO_TALK_TEST_REFRESH_TOKEN: string;

// META(facebook, instagram)
META_CLIENT_ID: string;
META_CLIENT_SECRET: string;
META_PARENT_BUSINESS_ID: string;
META_PARENT_BUSINESS_SYSTEM_USER_ACCESS_TOKEN: string;
META_APP_ID: string;

// NAVER
NAVER_CLIENT_ID: string;
NAVER_CLIENT_SECRET: string;
Expand All @@ -47,10 +67,6 @@ export namespace ConnectorGlobal {
// TYPEFORM
TYPEFORM_PERSONAL_ACCESS_KEY: string;

// FIGMA
FIGMA_TEST_SECRET: string;
FIGMA_TEST_FILE_KEY: string;

// ZOOM
ZOOM_TEST_REFRESH_TOKEN: string;
ZOOM_TEST_AUTHORIZATION_CODE: string;
Expand All @@ -60,18 +76,9 @@ export namespace ConnectorGlobal {
TEST_SWEET_TRACKER_KEY: string;
TEST_SWEET_TRACKER_T_INVOICE: string;

// KAKAO_TALK
KAKAO_TALK_CLIENT_ID: string;
KAKAO_TALK_CLIENT_SECRET: string;
KAKAO_TALK_TEST_REFRESH_TOKEN: string;

// KOREA_EXIM_BANK (한국수출입은행)
KOREA_EXIM_BANK_API_KEY: string;

// IMWEB
IMWEB_TEST_API_KEY: string;
IMWEB_TEST_API_SECRET: string;

// STABILITY AI
STABILITY_AI_API_KEY: string;
STABILITY_AI_HOST: string & tags.Format<"uri">;
Expand Down
7 changes: 7 additions & 0 deletions src/api/structures/connector/meta/IMeta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export namespace IMeta {
export interface AccessTokenDto {
access_token: string;
token_type: "bearer";
expires_in: number;
}
}
34 changes: 34 additions & 0 deletions src/controllers/connector/meta/metaController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import core from "@nestia/core";
import { Controller, Query } from "@nestjs/common";

import { IMeta } from "@wrtn/connector-api/lib/structures/connector/meta/IMeta";

import { ConnectorGlobal } from "../../../ConnectorGlobal";
import { MetaProvider } from "../../../providers/connector/meta/MetaProvider";

@Controller("connector/meta")
export class MetaController {
/**
* Meta login 인증창을 통해 유저가 로그인하면 콜백으로 호출되는 API.
*
* @internal
*
* @param query OAuth2 authorization code.
* @returns access token DTO.
*/
@core.TypedRoute.Get("auth")
async authorization(
@Query() query: { code: string; state: string },
): Promise<IMeta.AccessTokenDto> {
const authorizationCode = query.code;

const res = await MetaProvider.getAccessToken({
client_id: ConnectorGlobal.env.META_CLIENT_ID,
client_secret: ConnectorGlobal.env.META_CLIENT_SECRET,
code: authorizationCode,
redirect_uri: "http://localhost:3003/connector/meta/auth",
});

return res;
}
}
8 changes: 8 additions & 0 deletions src/controllers/connector/meta/metaModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Module } from "@nestjs/common";

import { MetaController } from "./metaController";

@Module({
controllers: [MetaController],
})
export class MetaModule {}
97 changes: 97 additions & 0 deletions src/providers/connector/meta/MetaProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import axios from "axios";
import { createHmac } from "crypto";

import { ConnectorGlobal } from "../../../ConnectorGlobal";

export namespace MetaProvider {
export const baseUrl = "https://graph.facebook.com/v20.0/";

export async function getAccessToken(options: {
client_id: string;
client_secret: string;
redirect_uri: string;
code: string;
}) {
const queryParams = Object.entries(options)
.map(([key, value]) => `${key}=${value}`)
.join("&");

const res = await axios.get(
`https://graph.facebook.com/oauth/access_token?${queryParams}`,
);

return res.data;
}

export async function setAgency(input: {
businessId: string; // client business id
userAccessToken: string; // user access token
}) {
const PARTNER_BM_ID = ConnectorGlobal.env.META_PARENT_BUSINESS_ID;
const url = `${MetaProvider.baseUrl}/${PARTNER_BM_ID}/managed_businesses?existing_client_business_id=${input.businessId}&access_token=${input.userAccessToken}`;
const res = await axios.post(url);
return res.data;
}

export async function getSystemUserAccessToken(input: {
businessId: string; // client business id
userAccessToken: string; // user access token
}) {
const pbmAcessToken = `${ConnectorGlobal.env.META_PARENT_BUSINESS_SYSTEM_USER_ACCESS_TOKEN}`;
const appId = `${ConnectorGlobal.env.META_APP_ID}`;
const url = `${MetaProvider.baseUrl}/${input.businessId}/access_token?scope=ads_management,pages_read_engagement&app_id=${appId}&access_token=${pbmAcessToken}`;
const res = await axios.post(url);
return res.data;
}

/**
*
* @param input 조회할 대상의 액세스 토큰을 담은 요청 DTO.
* @returns
*/
export async function getUserId(input: {
/**
* 조회할 대상의 액세스 토큰으로 타입이 유저가 아닌 모든 것을 포함한다.
*/
accessToken: string;
}) {
const url = `${baseUrl}/me?access_token=${input.accessToken}`;
const res = await axios.get(url);
return res.data;
}

export async function assign(input: {
/**
* 할당할 자신의 아이디
*/
assetId: string;

/**
* 할당받을 유저의 아이디 ( 여기서는 시스템 유저에 해당 )
*/
userId: string;

accessToken: string;
}) {
const url = `${baseUrl}/${input.assetId}/assigned_users?user=${input.userId}&tasks=MANAGE&access_token=${input.accessToken}`;
const res = await axios.post(url);
return res.data;
}

export async function getPage(input: {
/**
* 권한을 가진 시스템 유저의 액세스 토큰
*/
systemUserAccessToken: string;
}) {
const url = `${MetaProvider.baseUrl}/me/accounts?access_token=${input.systemUserAccessToken}`;
const res = await axios.get(url);
return res.data;
}

export function makeAppSecretProof(accessToken: string) {
return createHmac("sha256", ConnectorGlobal.env.META_CLIENT_SECRET)
.update(accessToken)
.digest("hex");
}
}