From bf0e6c865a5b7a9ff4b81523ba9cf7a127812e95 Mon Sep 17 00:00:00 2001 From: cebgcontract <99630598+cebgcontract@users.noreply.github.com> Date: Thu, 8 Dec 2022 16:43:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0tiktok=E7=99=BB=E9=99=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/tiktok.controller.ts | 55 ++++++++++++++++++++++++++++ src/modules/Account.ts | 11 ++++++ src/net/NetClient.ts | 32 ++++++++++++++++ src/service/tiktok.svr.ts | 33 +++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 src/controllers/tiktok.controller.ts create mode 100644 src/net/NetClient.ts create mode 100644 src/service/tiktok.svr.ts diff --git a/src/controllers/tiktok.controller.ts b/src/controllers/tiktok.controller.ts new file mode 100644 index 0000000..33b7869 --- /dev/null +++ b/src/controllers/tiktok.controller.ts @@ -0,0 +1,55 @@ +import BaseController from 'common/base.controller' +import { ZError } from 'common/ZError' +import { role, router } from 'decorators/router' +import { Account, PlatEnum } from 'modules/Account' +import { fetchAccessToken, refreshAccessToken } from 'service/tiktok.svr' +// 在tiktok的过期时间中, 减少一个小时 +const EXPIRE_REDUCE_SECOND = 3600 +class TiktokController extends BaseController { + @role('anon') + @router('post /wallet/login/tiktok') + async checkTiktokCode(req, res) { + let { code } = req.params + let result = await fetchAccessToken(code) + console.log(result) + if (!(result.message === 'success' && result.data?.error_code === 0)) { + throw new ZError(10, `${result.message}: ${result.data?.description} (${result.data?.error_code})`) + } + const openId = result.data['open_id'] + let user: any = {} + let now = Date.now() / 1000 + user.accessToken = result.data['access_token'] + user.refreshToken = result.data['refresh_token'] + user.accessTokenExpire = now + result.data['expires_in'] - EXPIRE_REDUCE_SECOND + user.refreshTokenExpire = now + result.data['refresh_expires_in'] - EXPIRE_REDUCE_SECOND + user.scope = result.data['scope'] + let account = await Account.insertOrUpdate({ plat: PlatEnum.TIKTOK, openId }, user) + const ztoken = await res.jwtSign({ id: account.id }) + return { token: ztoken } + } + @router('post /wallet/tiktok/accesstoken') + async getTiktokAccessToken(req, res) { + let user = req.user + let now = Date.now() / 1000 + if (user.accessToken && user.accessTokenExpire) { + if (now < user.accessTokenExpire) { + return { accessToken: user.accessToken } + } + } + if (user.refreshToken && user.refreshTokenExpire) { + if (now >= user.accessTokenExpire) { + throw new ZError(11, 'need login again') + } + } + let result = await refreshAccessToken(user.refreshToken) + if (!(result.message === 'success' && result.data?.error_code === 0)) { + throw new ZError(10, `${result.message}: ${result.data?.description} (${result.data?.error_code})`) + } + user.accessToken = result.data['access_token'] + user.refreshToken = result.data['refresh_token'] + user.accessTokenExpire = now + result.data['expires_in'] - EXPIRE_REDUCE_SECOND + user.refreshTokenExpire = now + result.data['refresh_expires_in'] - EXPIRE_REDUCE_SECOND + await user.save() + return { accessToken: user.accessToken } + } +} diff --git a/src/modules/Account.ts b/src/modules/Account.ts index b16b419..cda7bab 100644 --- a/src/modules/Account.ts +++ b/src/modules/Account.ts @@ -6,6 +6,7 @@ import { BaseModule } from './Base' export enum PlatEnum { GOOGLE = 0, APPLE = 1, + TIKTOK = 2, } interface AccountClass extends Base, TimeStamps {} @@ -37,6 +38,16 @@ class AccountClass extends BaseModule { public comment?: string @prop() public lastLogin?: Date + @prop() + public accessToken?: string + @prop() + public accessTokenExpire?: number + @prop() + public refreshToken?: string + @prop() + public refreshTokenExpire?: number + @prop() + public scope?: string } export const Account = getModelForClass(AccountClass, { existingConnection: AccountClass.db }) diff --git a/src/net/NetClient.ts b/src/net/NetClient.ts new file mode 100644 index 0000000..b2a6839 --- /dev/null +++ b/src/net/NetClient.ts @@ -0,0 +1,32 @@ +import axios, { AxiosRequestConfig } from 'axios' +export interface IReqData { + url: string + method?: string + data?: any +} +export class NetClient { + httpGet(reqData: IReqData | string): Promise { + let opt: AxiosRequestConfig = { method: 'get' } + if (typeof reqData == 'string') { + opt.url = reqData + } else { + Object.assign(opt, reqData) + } + return this.request(opt) + } + httpPost(data: IReqData): Promise { + let reqData: AxiosRequestConfig = { + method: 'post', + } + Object.assign(reqData, data) + return this.request(reqData) + } + + request(data: AxiosRequestConfig): Promise { + let defaultCfg: AxiosRequestConfig = { + method: 'get', + } + Object.assign(defaultCfg, data) + return axios(defaultCfg).then(res => res.data) + } +} diff --git a/src/service/tiktok.svr.ts b/src/service/tiktok.svr.ts new file mode 100644 index 0000000..b01766f --- /dev/null +++ b/src/service/tiktok.svr.ts @@ -0,0 +1,33 @@ +import { NetClient } from 'net/NetClient' + +const TIKTOK_ACCESS_TOKEN_URL = 'https://open-api.tiktok.com/oauth/access_token/' + +const TIKTOK_REFRESH_TOKEN_URL = 'https://open-api.tiktok.com/oauth/refresh_token/' + +const TIKTOK_REVOKE_ACCESS_URL = 'https://open-api.tiktok.com/oauth/revoke/' + +const CLIENT_KEY = 'awqbuzh2qymmq8hs' +const CLIENT_SECRET = '12f6e52173e825fa04ff2c7d4480e28b' + +export function fetchAccessToken(code: string) { + let url_access_token = TIKTOK_ACCESS_TOKEN_URL + url_access_token += '?client_key=' + CLIENT_KEY + url_access_token += '&client_secret=' + CLIENT_SECRET + url_access_token += '&code=' + code + url_access_token += '&grant_type=authorization_code' + return new NetClient().httpPost({ + url: url_access_token, + method: 'post', + }) +} + +export function refreshAccessToken(refresh_token: string) { + let url_refresh_token = TIKTOK_REFRESH_TOKEN_URL + url_refresh_token += '?client_key=' + CLIENT_KEY + url_refresh_token += '&grant_type=refresh_token' + url_refresh_token += '&refresh_token=' + refresh_token + return new NetClient().httpPost({ + url: url_refresh_token, + method: 'post', + }) +}