拿掉 build files

This commit is contained in:
2022-07-18 16:33:23 +08:00
parent 41e2287bcb
commit 3707f158a3
31953 changed files with 0 additions and 4411796 deletions
-801
View File
@@ -1,801 +0,0 @@
import { Readable } from "stream";
import HTTPClient from "./http";
import * as Types from "./types";
import { AxiosResponse, AxiosRequestConfig } from "axios";
import { createMultipartFormData, ensureJSON, toArray } from "./utils";
type ChatType = "group" | "room";
type RequestOption = {
retryKey: string;
};
import {
MESSAGING_API_PREFIX,
DATA_API_PREFIX,
OAUTH_BASE_PREFIX,
OAUTH_BASE_PREFIX_V2_1,
} from "./endpoints";
export default class Client {
public config: Types.ClientConfig;
private http: HTTPClient;
private requestOption: Partial<RequestOption> = {};
constructor(config: Types.ClientConfig) {
if (!config.channelAccessToken) {
throw new Error("no channel access token");
}
this.config = config;
this.http = new HTTPClient({
defaultHeaders: {
Authorization: "Bearer " + this.config.channelAccessToken,
},
responseParser: this.parseHTTPResponse.bind(this),
...config.httpConfig,
});
}
public setRequestOptionOnce(option: Partial<RequestOption>) {
this.requestOption = option;
}
private generateRequestConfig(): Partial<AxiosRequestConfig> {
const config: Partial<AxiosRequestConfig> = { headers: {} };
if (this.requestOption.retryKey) {
config.headers["X-Line-Retry-Key"] = this.requestOption.retryKey;
}
// clear requestOption
this.requestOption = {};
return config;
}
private parseHTTPResponse(response: AxiosResponse) {
const { LINE_REQUEST_ID_HTTP_HEADER_NAME } = Types;
let resBody = {
...response.data,
};
if (response.headers[LINE_REQUEST_ID_HTTP_HEADER_NAME]) {
resBody[LINE_REQUEST_ID_HTTP_HEADER_NAME] =
response.headers[LINE_REQUEST_ID_HTTP_HEADER_NAME];
}
return resBody;
}
public pushMessage(
to: string,
messages: Types.Message | Types.Message[],
notificationDisabled: boolean = false,
): Promise<Types.MessageAPIResponseBase> {
return this.http.post(
`${MESSAGING_API_PREFIX}/message/push`,
{
messages: toArray(messages),
to,
notificationDisabled,
},
this.generateRequestConfig(),
);
}
public replyMessage(
replyToken: string,
messages: Types.Message | Types.Message[],
notificationDisabled: boolean = false,
): Promise<Types.MessageAPIResponseBase> {
return this.http.post(`${MESSAGING_API_PREFIX}/message/reply`, {
messages: toArray(messages),
replyToken,
notificationDisabled,
});
}
public async multicast(
to: string[],
messages: Types.Message | Types.Message[],
notificationDisabled: boolean = false,
): Promise<Types.MessageAPIResponseBase> {
return this.http.post(
`${MESSAGING_API_PREFIX}/message/multicast`,
{
messages: toArray(messages),
to,
notificationDisabled,
},
this.generateRequestConfig(),
);
}
public async narrowcast(
messages: Types.Message | Types.Message[],
recipient?: Types.ReceieptObject,
filter?: { demographic: Types.DemographicFilterObject },
limit?: { max?: number; upToRemainingQuota?: boolean },
notificationDisabled?: boolean,
): Promise<Types.MessageAPIResponseBase> {
return this.http.post(
`${MESSAGING_API_PREFIX}/message/narrowcast`,
{
messages: toArray(messages),
recipient,
filter,
limit,
notificationDisabled,
},
this.generateRequestConfig(),
);
}
public async broadcast(
messages: Types.Message | Types.Message[],
notificationDisabled: boolean = false,
): Promise<Types.MessageAPIResponseBase> {
return this.http.post(
`${MESSAGING_API_PREFIX}/message/broadcast`,
{
messages: toArray(messages),
notificationDisabled,
},
this.generateRequestConfig(),
);
}
public async getProfile(userId: string): Promise<Types.Profile> {
const profile = await this.http.get<Types.Profile>(
`${MESSAGING_API_PREFIX}/profile/${userId}`,
);
return ensureJSON(profile);
}
private async getChatMemberProfile(
chatType: ChatType,
chatId: string,
userId: string,
): Promise<Types.Profile> {
const profile = await this.http.get<Types.Profile>(
`${MESSAGING_API_PREFIX}/${chatType}/${chatId}/member/${userId}`,
);
return ensureJSON(profile);
}
public async getGroupMemberProfile(
groupId: string,
userId: string,
): Promise<Types.Profile> {
return this.getChatMemberProfile("group", groupId, userId);
}
public async getRoomMemberProfile(
roomId: string,
userId: string,
): Promise<Types.Profile> {
return this.getChatMemberProfile("room", roomId, userId);
}
private async getChatMemberIds(
chatType: ChatType,
chatId: string,
): Promise<string[]> {
let memberIds: string[] = [];
let start: string;
do {
const res = await this.http.get<{ memberIds: string[]; next?: string }>(
`${MESSAGING_API_PREFIX}/${chatType}/${chatId}/members/ids`,
start ? { start } : null,
);
ensureJSON(res);
memberIds = memberIds.concat(res.memberIds);
start = res.next;
} while (start);
return memberIds;
}
public async getGroupMemberIds(groupId: string): Promise<string[]> {
return this.getChatMemberIds("group", groupId);
}
public async getRoomMemberIds(roomId: string): Promise<string[]> {
return this.getChatMemberIds("room", roomId);
}
public async getBotFollowersIds(): Promise<string[]> {
let userIds: string[] = [];
let start: string;
do {
const res = await this.http.get<{ userIds: string[]; next?: string }>(
`${MESSAGING_API_PREFIX}/followers/ids`,
start ? { start, limit: 1000 } : { limit: 1000 },
);
ensureJSON(res);
userIds = userIds.concat(res.userIds);
start = res.next;
} while (start);
return userIds;
}
public async getGroupMembersCount(
groupId: string,
): Promise<Types.MembersCountResponse> {
const groupMemberCount = await this.http.get<Types.MembersCountResponse>(
`${MESSAGING_API_PREFIX}/group/${groupId}/members/count`,
);
return ensureJSON(groupMemberCount);
}
public async getRoomMembersCount(
roomId: string,
): Promise<Types.MembersCountResponse> {
const roomMemberCount = await this.http.get<Types.MembersCountResponse>(
`${MESSAGING_API_PREFIX}/room/${roomId}/members/count`,
);
return ensureJSON(roomMemberCount);
}
public async getGroupSummary(
groupId: string,
): Promise<Types.GroupSummaryResponse> {
const groupSummary = await this.http.get<Types.GroupSummaryResponse>(
`${MESSAGING_API_PREFIX}/group/${groupId}/summary`,
);
return ensureJSON(groupSummary);
}
public async getMessageContent(messageId: string): Promise<Readable> {
return this.http.getStream(
`${DATA_API_PREFIX}/message/${messageId}/content`,
);
}
private leaveChat(chatType: ChatType, chatId: string): Promise<any> {
return this.http.post(
`${MESSAGING_API_PREFIX}/${chatType}/${chatId}/leave`,
);
}
public async leaveGroup(groupId: string): Promise<any> {
return this.leaveChat("group", groupId);
}
public async leaveRoom(roomId: string): Promise<any> {
return this.leaveChat("room", roomId);
}
public async getRichMenu(
richMenuId: string,
): Promise<Types.RichMenuResponse> {
const res = await this.http.get<Types.RichMenuResponse>(
`${MESSAGING_API_PREFIX}/richmenu/${richMenuId}`,
);
return ensureJSON(res);
}
public async createRichMenu(richMenu: Types.RichMenu): Promise<string> {
const res = await this.http.post<any>(
`${MESSAGING_API_PREFIX}/richmenu`,
richMenu,
);
return ensureJSON(res).richMenuId;
}
public async deleteRichMenu(richMenuId: string): Promise<any> {
return this.http.delete(`${MESSAGING_API_PREFIX}/richmenu/${richMenuId}`);
}
public async getRichMenuAliasList(): Promise<Types.GetRichMenuAliasListResponse> {
const res = await this.http.get<Types.GetRichMenuAliasListResponse>(
`${MESSAGING_API_PREFIX}/richmenu/alias/list`,
);
return ensureJSON(res);
}
public async getRichMenuAlias(
richMenuAliasId: string,
): Promise<Types.GetRichMenuAliasResponse> {
const res = await this.http.get<Types.GetRichMenuAliasResponse>(
`${MESSAGING_API_PREFIX}/richmenu/alias/${richMenuAliasId}`,
);
return ensureJSON(res);
}
public async createRichMenuAlias(
richMenuId: string,
richMenuAliasId: string,
): Promise<{}> {
const res = await this.http.post<{}>(
`${MESSAGING_API_PREFIX}/richmenu/alias`,
{
richMenuId,
richMenuAliasId,
},
);
return ensureJSON(res);
}
public async deleteRichMenuAlias(richMenuAliasId: string): Promise<{}> {
const res = this.http.delete<{}>(
`${MESSAGING_API_PREFIX}/richmenu/alias/${richMenuAliasId}`,
);
return ensureJSON(res);
}
public async updateRichMenuAlias(
richMenuAliasId: string,
richMenuId: string,
): Promise<{}> {
const res = await this.http.post<{}>(
`${MESSAGING_API_PREFIX}/richmenu/alias/${richMenuAliasId}`,
{
richMenuId,
},
);
return ensureJSON(res);
}
public async getRichMenuIdOfUser(userId: string): Promise<string> {
const res = await this.http.get<any>(
`${MESSAGING_API_PREFIX}/user/${userId}/richmenu`,
);
return ensureJSON(res).richMenuId;
}
public async linkRichMenuToUser(
userId: string,
richMenuId: string,
): Promise<any> {
return this.http.post(
`${MESSAGING_API_PREFIX}/user/${userId}/richmenu/${richMenuId}`,
);
}
public async unlinkRichMenuFromUser(userId: string): Promise<any> {
return this.http.delete(`${MESSAGING_API_PREFIX}/user/${userId}/richmenu`);
}
public async linkRichMenuToMultipleUsers(
richMenuId: string,
userIds: string[],
): Promise<any> {
return this.http.post(`${MESSAGING_API_PREFIX}/richmenu/bulk/link`, {
richMenuId,
userIds,
});
}
public async unlinkRichMenusFromMultipleUsers(
userIds: string[],
): Promise<any> {
return this.http.post(`${MESSAGING_API_PREFIX}/richmenu/bulk/unlink`, {
userIds,
});
}
public async getRichMenuImage(richMenuId: string): Promise<Readable> {
return this.http.getStream(
`${DATA_API_PREFIX}/richmenu/${richMenuId}/content`,
);
}
public async setRichMenuImage(
richMenuId: string,
data: Buffer | Readable,
contentType?: string,
): Promise<any> {
return this.http.postBinary(
`${DATA_API_PREFIX}/richmenu/${richMenuId}/content`,
data,
contentType,
);
}
public async getRichMenuList(): Promise<Array<Types.RichMenuResponse>> {
const res = await this.http.get<any>(
`${MESSAGING_API_PREFIX}/richmenu/list`,
);
return ensureJSON(res).richmenus;
}
public async setDefaultRichMenu(richMenuId: string): Promise<{}> {
return this.http.post(
`${MESSAGING_API_PREFIX}/user/all/richmenu/${richMenuId}`,
);
}
public async getDefaultRichMenuId(): Promise<string> {
const res = await this.http.get<any>(
`${MESSAGING_API_PREFIX}/user/all/richmenu`,
);
return ensureJSON(res).richMenuId;
}
public async deleteDefaultRichMenu(): Promise<{}> {
return this.http.delete(`${MESSAGING_API_PREFIX}/user/all/richmenu`);
}
public async getLinkToken(userId: string): Promise<string> {
const res = await this.http.post<any>(
`${MESSAGING_API_PREFIX}/user/${userId}/linkToken`,
);
return ensureJSON(res).linkToken;
}
public async getNumberOfSentReplyMessages(
date: string,
): Promise<Types.NumberOfMessagesSentResponse> {
const res = await this.http.get<Types.NumberOfMessagesSentResponse>(
`${MESSAGING_API_PREFIX}/message/delivery/reply?date=${date}`,
);
return ensureJSON(res);
}
public async getNumberOfSentPushMessages(
date: string,
): Promise<Types.NumberOfMessagesSentResponse> {
const res = await this.http.get<Types.NumberOfMessagesSentResponse>(
`${MESSAGING_API_PREFIX}/message/delivery/push?date=${date}`,
);
return ensureJSON(res);
}
public async getNumberOfSentMulticastMessages(
date: string,
): Promise<Types.NumberOfMessagesSentResponse> {
const res = await this.http.get<Types.NumberOfMessagesSentResponse>(
`${MESSAGING_API_PREFIX}/message/delivery/multicast?date=${date}`,
);
return ensureJSON(res);
}
public async getNarrowcastProgress(
requestId: string,
): Promise<Types.NarrowcastProgressResponse> {
const res = await this.http.get<Types.NarrowcastProgressResponse>(
`${MESSAGING_API_PREFIX}/message/progress/narrowcast?requestId=${requestId}`,
);
return ensureJSON(res);
}
public async getTargetLimitForAdditionalMessages(): Promise<Types.TargetLimitForAdditionalMessages> {
const res = await this.http.get<Types.TargetLimitForAdditionalMessages>(
`${MESSAGING_API_PREFIX}/message/quota`,
);
return ensureJSON(res);
}
public async getNumberOfMessagesSentThisMonth(): Promise<Types.NumberOfMessagesSentThisMonth> {
const res = await this.http.get<Types.NumberOfMessagesSentThisMonth>(
`${MESSAGING_API_PREFIX}/message/quota/consumption`,
);
return ensureJSON(res);
}
public async getNumberOfSentBroadcastMessages(
date: string,
): Promise<Types.NumberOfMessagesSentResponse> {
const res = await this.http.get<Types.NumberOfMessagesSentResponse>(
`${MESSAGING_API_PREFIX}/message/delivery/broadcast?date=${date}`,
);
return ensureJSON(res);
}
public async getNumberOfMessageDeliveries(
date: string,
): Promise<Types.NumberOfMessageDeliveriesResponse> {
const res = await this.http.get<Types.NumberOfMessageDeliveriesResponse>(
`${MESSAGING_API_PREFIX}/insight/message/delivery?date=${date}`,
);
return ensureJSON(res);
}
public async getNumberOfFollowers(
date: string,
): Promise<Types.NumberOfFollowersResponse> {
const res = await this.http.get<Types.NumberOfFollowersResponse>(
`${MESSAGING_API_PREFIX}/insight/followers?date=${date}`,
);
return ensureJSON(res);
}
public async getFriendDemographics(): Promise<Types.FriendDemographics> {
const res = await this.http.get<Types.FriendDemographics>(
`${MESSAGING_API_PREFIX}/insight/demographic`,
);
return ensureJSON(res);
}
public async getUserInteractionStatistics(
requestId: string,
): Promise<Types.UserInteractionStatistics> {
const res = await this.http.get<Types.UserInteractionStatistics>(
`${MESSAGING_API_PREFIX}/insight/message/event?requestId=${requestId}`,
);
return ensureJSON(res);
}
public async createUploadAudienceGroup(uploadAudienceGroup: {
description: string;
isIfaAudience?: boolean;
audiences?: { id: string }[];
uploadDescription?: string;
}) {
const res = await this.http.post<{
audienceGroupId: number;
type: string;
description: string;
created: number;
}>(`${MESSAGING_API_PREFIX}/audienceGroup/upload`, {
...uploadAudienceGroup,
});
return ensureJSON(res);
}
public async createUploadAudienceGroupByFile(uploadAudienceGroup: {
description: string;
isIfaAudience?: boolean;
uploadDescription?: string;
file: Buffer | Readable;
}) {
const file = await this.http.toBuffer(uploadAudienceGroup.file);
const body = createMultipartFormData({ ...uploadAudienceGroup, file });
const res = await this.http.post<{
audienceGroupId: number;
type: "UPLOAD";
description: string;
created: number;
}>(`${DATA_API_PREFIX}/audienceGroup/upload/byFile`, body, {
headers: body.getHeaders(),
});
return ensureJSON(res);
}
public async updateUploadAudienceGroup(
uploadAudienceGroup: {
audienceGroupId: number;
description?: string;
uploadDescription?: string;
audiences: { id: string }[];
},
// for set request timeout
httpConfig?: Partial<AxiosRequestConfig>,
) {
const res = await this.http.put<{}>(
`${MESSAGING_API_PREFIX}/audienceGroup/upload`,
{
...uploadAudienceGroup,
},
httpConfig,
);
return ensureJSON(res);
}
public async updateUploadAudienceGroupByFile(
uploadAudienceGroup: {
audienceGroupId: number;
uploadDescription?: string;
file: Buffer | Readable;
},
// for set request timeout
httpConfig?: Partial<AxiosRequestConfig>,
) {
const file = await this.http.toBuffer(uploadAudienceGroup.file);
const body = createMultipartFormData({ ...uploadAudienceGroup, file });
const res = await this.http.put<{}>(
`${DATA_API_PREFIX}/audienceGroup/upload/byFile`,
body,
{ headers: body.getHeaders(), ...httpConfig },
);
return ensureJSON(res);
}
public async createClickAudienceGroup(clickAudienceGroup: {
description: string;
requestId: string;
clickUrl?: string;
}) {
const res = await this.http.post<
{
audienceGroupId: number;
type: string;
created: number;
} & typeof clickAudienceGroup
>(`${MESSAGING_API_PREFIX}/audienceGroup/click`, {
...clickAudienceGroup,
});
return ensureJSON(res);
}
public async createImpAudienceGroup(impAudienceGroup: {
requestId: string;
description: string;
}) {
const res = await this.http.post<
{
audienceGroupId: number;
type: string;
created: number;
} & typeof impAudienceGroup
>(`${MESSAGING_API_PREFIX}/audienceGroup/imp`, {
...impAudienceGroup,
});
return ensureJSON(res);
}
public async setDescriptionAudienceGroup(
description: string,
audienceGroupId: string,
) {
const res = await this.http.put<{}>(
`${MESSAGING_API_PREFIX}/audienceGroup/${audienceGroupId}/updateDescription`,
{
description,
},
);
return ensureJSON(res);
}
public async deleteAudienceGroup(audienceGroupId: string) {
const res = await this.http.delete<{}>(
`${MESSAGING_API_PREFIX}/audienceGroup/${audienceGroupId}`,
);
return ensureJSON(res);
}
public async getAudienceGroup(audienceGroupId: string) {
const res = await this.http.get<Types.AudienceGroup>(
`${MESSAGING_API_PREFIX}/audienceGroup/${audienceGroupId}`,
);
return ensureJSON(res);
}
public async getAudienceGroups(
page: number,
description?: string,
status?: Types.AudienceGroupStatus,
size?: number,
createRoute?: Types.AudienceGroupCreateRoute,
includesExternalPublicGroups?: boolean,
) {
const res = await this.http.get<{
audienceGroups: Types.AudienceGroups;
hasNextPage: boolean;
totalCount: number;
readWriteAudienceGroupTotalCount: number;
page: number;
size: number;
}>(`${MESSAGING_API_PREFIX}/audienceGroup/list`, {
page,
description,
status,
size,
createRoute,
includesExternalPublicGroups,
});
return ensureJSON(res);
}
public async getAudienceGroupAuthorityLevel() {
const res = await this.http.get<{
authorityLevel: Types.AudienceGroupAuthorityLevel;
}>(`${MESSAGING_API_PREFIX}/audienceGroup/authorityLevel`);
return ensureJSON(res);
}
public async changeAudienceGroupAuthorityLevel(
authorityLevel: Types.AudienceGroupAuthorityLevel,
) {
const res = await this.http.put<{}>(
`${MESSAGING_API_PREFIX}/audienceGroup/authorityLevel`,
{ authorityLevel },
);
return ensureJSON(res);
}
public async getBotInfo(): Promise<Types.BotInfoResponse> {
const res = await this.http.get<Types.BotInfoResponse>(
`${MESSAGING_API_PREFIX}/info`,
);
return ensureJSON(res);
}
public async setWebhookEndpointUrl(endpoint: string) {
return this.http.put<{}>(
`${MESSAGING_API_PREFIX}/channel/webhook/endpoint`,
{ endpoint },
);
}
public async getWebhookEndpointInfo() {
const res = await this.http.get<Types.WebhookEndpointInfoResponse>(
`${MESSAGING_API_PREFIX}/channel/webhook/endpoint`,
);
return ensureJSON(res);
}
public async testWebhookEndpoint(endpoint?: string) {
const res = await this.http.post<Types.TestWebhookEndpointResponse>(
`${MESSAGING_API_PREFIX}/channel/webhook/test`,
{ endpoint },
);
return ensureJSON(res);
}
}
export class OAuth {
private http: HTTPClient;
constructor() {
this.http = new HTTPClient();
}
public issueAccessToken(
client_id: string,
client_secret: string,
): Promise<Types.ChannelAccessToken> {
return this.http.postForm(`${OAUTH_BASE_PREFIX}/accessToken`, {
grant_type: "client_credentials",
client_id,
client_secret,
});
}
public revokeAccessToken(access_token: string): Promise<{}> {
return this.http.postForm(`${OAUTH_BASE_PREFIX}/revoke`, { access_token });
}
public verifyAccessToken(
access_token: string,
): Promise<Types.VerifyAccessToken> {
return this.http.get(`${OAUTH_BASE_PREFIX_V2_1}/verify`, { access_token });
}
public verifyIdToken(
id_token: string,
client_id: string,
nonce?: string,
user_id?: string,
): Promise<Types.VerifyIDToken> {
return this.http.postForm(`${OAUTH_BASE_PREFIX}/verify`, {
id_token,
client_id,
nonce,
user_id,
});
}
public issueChannelAccessTokenV2_1(
client_assertion: string,
): Promise<Types.ChannelAccessToken> {
return this.http.postForm(`${OAUTH_BASE_PREFIX_V2_1}/token`, {
grant_type: "client_credentials",
client_assertion_type:
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion,
});
}
public getChannelAccessTokenKeyIdsV2_1(
client_assertion: string,
): Promise<{ key_ids: string[] }> {
return this.http.get(`${OAUTH_BASE_PREFIX_V2_1}/tokens/kid`, {
client_assertion_type:
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion,
});
}
public revokeChannelAccessTokenV2_1(
client_id: string,
client_secret: string,
access_token: string,
): Promise<{}> {
return this.http.postForm(`${OAUTH_BASE_PREFIX_V2_1}/revoke`, {
client_id,
client_secret,
access_token,
});
}
}
-4
View File
@@ -1,4 +0,0 @@
export const MESSAGING_API_PREFIX = `https://api.line.me/v2/bot`;
export const DATA_API_PREFIX = `https://api-data.line.me/v2/bot`;
export const OAUTH_BASE_PREFIX = `https://api.line.me/v2/oauth`;
export const OAUTH_BASE_PREFIX_V2_1 = `https://api.line.me/oauth2/v2.1`;
-38
View File
@@ -1,38 +0,0 @@
export class SignatureValidationFailed extends Error {
constructor(message: string, public signature?: string) {
super(message);
}
}
export class JSONParseError extends Error {
constructor(message: string, public raw: any) {
super(message);
}
}
export class RequestError extends Error {
constructor(
message: string,
public code: string,
private originalError: Error,
) {
super(message);
}
}
export class ReadError extends Error {
constructor(private originalError: Error) {
super(originalError.message);
}
}
export class HTTPError extends Error {
constructor(
message: string,
public statusCode: number,
public statusMessage: string,
public originalError: any,
) {
super(message);
}
}
-158
View File
@@ -1,158 +0,0 @@
import axios, {
AxiosInstance,
AxiosError,
AxiosResponse,
AxiosRequestConfig,
} from "axios";
import { Readable } from "stream";
import { HTTPError, ReadError, RequestError } from "./exceptions";
import * as fileType from "file-type";
import * as qs from "querystring";
const pkg = require("../package.json");
interface httpClientConfig extends Partial<AxiosRequestConfig> {
baseURL?: string;
defaultHeaders?: any;
responseParser?: <T>(res: AxiosResponse) => T;
}
export default class HTTPClient {
private instance: AxiosInstance;
private config: httpClientConfig;
constructor(config: httpClientConfig = {}) {
this.config = config;
const { baseURL, defaultHeaders } = config;
this.instance = axios.create({
baseURL,
headers: Object.assign({}, defaultHeaders, {
"User-Agent": `${pkg.name}/${pkg.version}`,
}),
});
this.instance.interceptors.response.use(
res => res,
err => Promise.reject(this.wrapError(err)),
);
}
public async get<T>(url: string, params?: any): Promise<T> {
const res = await this.instance.get(url, { params });
return res.data;
}
public async getStream(url: string, params?: any): Promise<Readable> {
const res = await this.instance.get(url, {
params,
responseType: "stream",
});
return res.data as Readable;
}
public async post<T>(
url: string,
body?: any,
config?: Partial<AxiosRequestConfig>,
): Promise<T> {
const res = await this.instance.post(url, body, {
headers: {
"Content-Type": "application/json",
...(config && config.headers),
},
...config,
});
return this.responseParse(res);
}
private responseParse(res: AxiosResponse) {
const { responseParser } = this.config;
if (responseParser) return responseParser(res);
else return res.data;
}
public async put<T>(
url: string,
body?: any,
config?: Partial<AxiosRequestConfig>,
): Promise<T> {
const res = await this.instance.put(url, body, {
headers: {
"Content-Type": "application/json",
...(config && config.headers),
},
...config,
});
return this.responseParse(res);
}
public async postForm<T>(url: string, body?: any): Promise<T> {
const res = await this.instance.post(url, qs.stringify(body), {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
return res.data;
}
public async toBuffer(data: Buffer | Readable) {
if (Buffer.isBuffer(data)) {
return data;
} else if (data instanceof Readable) {
return await new Promise<Buffer>((resolve, reject) => {
const buffers: Buffer[] = [];
let size = 0;
data.on("data", (chunk: Buffer) => {
buffers.push(chunk);
size += chunk.length;
});
data.on("end", () => resolve(Buffer.concat(buffers, size)));
data.on("error", reject);
});
} else {
throw new Error("invalid data type for binary data");
}
}
public async postBinary<T>(
url: string,
data: Buffer | Readable,
contentType?: string,
): Promise<T> {
const buffer = await this.toBuffer(data);
const res = await this.instance.post(url, buffer, {
headers: {
"Content-Type": contentType || (await fileType.fromBuffer(buffer)).mime,
"Content-Length": buffer.length,
},
});
return res.data;
}
public async delete<T>(url: string, params?: any): Promise<T> {
const res = await this.instance.delete(url, { params });
return res.data;
}
private wrapError(err: AxiosError): Error {
if (err.response) {
return new HTTPError(
err.message,
err.response.status,
err.response.statusText,
err,
);
} else if (err.code) {
return new RequestError(err.message, err.code, err);
} else if (err.config) {
// unknown, but from axios
return new ReadError(err);
}
// otherwise, just rethrow
return err;
}
}
-9
View File
@@ -1,9 +0,0 @@
import Client, { OAuth } from "./client";
import middleware from "./middleware";
import validateSignature from "./validate-signature";
export { Client, middleware, validateSignature, OAuth };
// re-export exceptions and types
export * from "./exceptions";
export * from "./types";
-75
View File
@@ -1,75 +0,0 @@
import { raw } from "body-parser";
import * as http from "http";
import { JSONParseError, SignatureValidationFailed } from "./exceptions";
import * as Types from "./types";
import validateSignature from "./validate-signature";
export type Request = http.IncomingMessage & { body: any };
export type Response = http.ServerResponse;
export type NextCallback = (err?: Error) => void;
export type Middleware = (
req: Request,
res: Response,
next: NextCallback,
) => void | Promise<void>;
function isValidBody(body?: any): body is string | Buffer {
return (body && typeof body === "string") || Buffer.isBuffer(body);
}
export default function middleware(config: Types.MiddlewareConfig): Middleware {
if (!config.channelSecret) {
throw new Error("no channel secret");
}
const secret = config.channelSecret;
const _middleware: Middleware = async (req, res, next) => {
// header names are lower-cased
// https://nodejs.org/api/http.html#http_message_headers
const signature = req.headers[
Types.LINE_SIGNATURE_HTTP_HEADER_NAME
] as string;
if (!signature) {
next(new SignatureValidationFailed("no signature"));
return;
}
const body = await (async (): Promise<string | Buffer> => {
if (isValidBody((req as any).rawBody)) {
// rawBody is provided in Google Cloud Functions and others
return (req as any).rawBody;
} else if (isValidBody(req.body)) {
return req.body;
} else {
// body may not be parsed yet, parse it to a buffer
return new Promise<Buffer>((resolve, reject) =>
raw({ type: "*/*" })(req as any, res as any, (error: Error) =>
error ? reject(error) : resolve(req.body),
),
);
}
})();
if (!validateSignature(body, secret, signature)) {
next(
new SignatureValidationFailed("signature validation failed", signature),
);
return;
}
const strBody = Buffer.isBuffer(body) ? body.toString() : body;
try {
req.body = JSON.parse(strBody);
next();
} catch (err) {
next(new JSONParseError(err.message, strBody));
}
};
return (req, res, next): void => {
(<Promise<void>>_middleware(req, res, next)).catch(next);
};
}
-2851
View File
File diff suppressed because it is too large Load Diff
-29
View File
@@ -1,29 +0,0 @@
import { JSONParseError } from "./exceptions";
import * as FormData from "form-data";
export function toArray<T>(maybeArr: T | T[]): T[] {
return Array.isArray(maybeArr) ? maybeArr : [maybeArr];
}
export function ensureJSON<T>(raw: T): T {
if (typeof raw === "object") {
return raw;
} else {
throw new JSONParseError("Failed to parse response body as JSON", raw);
}
}
export function createMultipartFormData(
this: FormData | void,
formBody: Record<string, any>,
): FormData {
const formData = this instanceof FormData ? this : new FormData();
Object.entries(formBody).forEach(([key, value]) => {
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
formData.append(key, value);
} else {
formData.append(key, String(value));
}
});
return formData;
}
-23
View File
@@ -1,23 +0,0 @@
import { createHmac, timingSafeEqual } from "crypto";
function s2b(str: string, encoding: BufferEncoding): Buffer {
return Buffer.from(str, encoding);
}
function safeCompare(a: Buffer, b: Buffer): boolean {
if (a.length !== b.length) {
return false;
}
return timingSafeEqual(a, b);
}
export default function validateSignature(
body: string | Buffer,
channelSecret: string,
signature: string,
): boolean {
return safeCompare(
createHmac("SHA256", channelSecret).update(body).digest(),
s2b(signature, "base64"),
);
}