refs #69 Add support proxy connection for REST endpoint

This commit is contained in:
AkiraFukushima 2019-10-18 22:45:05 +09:00
parent f27cff119c
commit 4d143639cb
8 changed files with 246 additions and 60 deletions

View file

@ -0,0 +1,21 @@
import Mastodon, { Instance, ProxyConfig } from 'megalodon'
declare var process: {
env: {
PROXY_HOST: string
PROXY_PORT: number
PROXY_PROTOCOL: string
}
}
const BASE_URL: string = 'http://mastodon.social'
const proxy: ProxyConfig = {
host: process.env.PROXY_HOST,
port: process.env.PROXY_PORT,
protocol: process.env.PROXY_PROTOCOL
}
Mastodon.get<Instance>('/api/v1/instance', {}, BASE_URL, proxy).then(res => {
console.log(res)
})

View file

@ -0,0 +1,26 @@
import Mastodon, { Status, Response, ProxyConfig } from 'megalodon'
declare var process: {
env: {
MASTODON_ACCESS_TOKEN: string
PROXY_HOST: string
PROXY_PORT: number
PROXY_PROTOCOL: string
}
}
const BASE_URL: string = 'https://mastodon.social'
const access_token: string = process.env.MASTODON_ACCESS_TOKEN
const proxy: ProxyConfig = {
host: process.env.PROXY_HOST,
port: process.env.PROXY_PORT,
protocol: process.env.PROXY_PROTOCOL
}
const client = new Mastodon(access_token, BASE_URL + '/api/v1', 'megalodon', proxy)
client.get<Array<Status>>('/timelines/public').then((resp: Response<Array<Status>>) => {
console.log(resp.data)
})

View file

@ -12,6 +12,6 @@ const access_token: string = process.env.MASTODON_ACCESS_TOKEN
const client = new Mastodon(access_token, BASE_URL + '/api/v1')
client.get<Array<Status>>('/timelines/home').then((resp: Response<Array<Status>>) => {
client.get<Array<Status>>('/timelines/public').then((resp: Response<Array<Status>>) => {
console.log(resp.data)
})

View file

@ -48,6 +48,13 @@
dependencies:
"@types/node" "*"
agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies:
es6-promisify "^5.0.0"
ajv@^5.3.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
@ -153,6 +160,13 @@ debug@=3.1.0:
dependencies:
ms "2.0.0"
debug@^3.1.0:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
dependencies:
ms "^2.1.1"
debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
@ -173,6 +187,18 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
es6-promise@^4.0.3:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
dependencies:
es6-promise "^4.0.3"
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
@ -267,6 +293,14 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
https-proxy-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz#0106efa5d63d6d6f3ab87c999fa4877a3fd1ff97"
integrity sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==
dependencies:
agent-base "^4.3.0"
debug "^3.1.0"
is-buffer@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
@ -337,6 +371,7 @@ log4js@^5.2.2:
"@types/request" "^2.47.0"
"@types/ws" "^6.0.1"
axios "^0.18.1"
https-proxy-agent "^3.0.0"
moment "^2.24.0"
oauth "^0.9.15"
request "^2.87.0"

View file

@ -54,6 +54,7 @@
"@types/request": "^2.47.0",
"@types/ws": "^6.0.1",
"axios": "^0.18.1",
"https-proxy-agent": "^3.0.0",
"moment": "^2.24.0",
"oauth": "^0.9.15",
"request": "^2.87.0",

View file

@ -1,4 +1,4 @@
import Mastodon, { MegalodonInstance } from './mastodon'
import Mastodon, { MegalodonInstance, ProxyConfig } from './mastodon'
import StreamListener from './stream_listener'
import WebSocket from './web_socket'
import Response from './response'
@ -43,6 +43,7 @@ export {
MegalodonInstance,
RequestCanceledError,
isCancel,
ProxyConfig,
/**
* Entities
*/

View file

@ -1,5 +1,6 @@
import { OAuth2 } from 'oauth'
import axios, { AxiosResponse, CancelTokenSource } from 'axios'
import HttpsProxyAgent from 'https-proxy-agent'
import StreamListener from './stream_listener'
import WebSocket from './web_socket'
@ -26,6 +27,16 @@ export interface MegalodonInstance {
socket(path: string, strea: string): WebSocket
}
export type ProxyConfig = {
host: string
port: number
auth?: {
username: string
password: string
}
protocol: string
}
/**
* Mastodon API client.
*
@ -40,16 +51,23 @@ export default class Mastodon implements MegalodonInstance {
private baseUrl: string
private userAgent: string
private cancelTokenSource: CancelTokenSource
private proxyConfig: ProxyConfig | false = false
/**
* @param accessToken access token from OAuth2 authorization
* @param baseUrl hostname or base URL
*/
constructor(accessToken: string, baseUrl = DEFAULT_URL, userAgent = DEFAULT_UA) {
constructor(
accessToken: string,
baseUrl: string = DEFAULT_URL,
userAgent: string = DEFAULT_UA,
proxyConfig: ProxyConfig | false = false
) {
this.accessToken = accessToken
this.baseUrl = baseUrl
this.userAgent = userAgent
this.cancelTokenSource = axios.CancelToken.source()
this.proxyConfig = proxyConfig
}
/**
@ -211,26 +229,46 @@ export default class Mastodon implements MegalodonInstance {
* @param params Query parameters
* @param baseUrl base URL of the target
*/
public static async get<T>(path: string, params = {}, baseUrl = DEFAULT_URL): Promise<Response<T>> {
public static async get<T>(
path: string,
params = {},
baseUrl = DEFAULT_URL,
proxyConfig: ProxyConfig | false = false
): Promise<Response<T>> {
const apiUrl = baseUrl
return axios
.get<T>(apiUrl + path, {
params
})
.then((resp: AxiosResponse<T>) => {
const res: Response<T> = {
data: resp.data,
status: resp.status,
statusText: resp.statusText,
headers: resp.headers
}
return res
let options = {
params: params
}
if (proxyConfig) {
options = Object.assign(options, {
httpsAgent: this._proxyAgent(proxyConfig)
})
}
return axios.get<T>(apiUrl + path, options).then((resp: AxiosResponse<T>) => {
const res: Response<T> = {
data: resp.data,
status: resp.status,
statusText: resp.statusText,
headers: resp.headers
}
return res
})
}
private static async _post<T>(path: string, params = {}, baseUrl = DEFAULT_URL): Promise<Response<T>> {
private static async _post<T>(
path: string,
params = {},
baseUrl = DEFAULT_URL,
proxyConfig: ProxyConfig | false = false
): Promise<Response<T>> {
let options = {}
if (proxyConfig) {
options = Object.assign(options, {
httpsAgent: this._proxyAgent(proxyConfig)
})
}
const apiUrl = baseUrl
return axios.post<T>(apiUrl + path, params).then((resp: AxiosResponse<T>) => {
return axios.post<T>(apiUrl + path, params, options).then((resp: AxiosResponse<T>) => {
const res: Response<T> = {
data: resp.data,
status: resp.status,
@ -247,14 +285,20 @@ export default class Mastodon implements MegalodonInstance {
* @param params Query parameters
*/
public async get<T>(path: string, params = {}): Promise<Response<T>> {
return axios
.get<T>(this.baseUrl + path, {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
},
params
let options = {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
},
params: params
}
if (this.proxyConfig) {
options = Object.assign(options, {
httpsAgent: Mastodon._proxyAgent(this.proxyConfig)
})
}
return axios
.get<T>(this.baseUrl + path, options)
.catch((err: Error) => {
if (axios.isCancel(err)) {
throw new RequestCanceledError(err.message)
@ -279,13 +323,19 @@ export default class Mastodon implements MegalodonInstance {
* @param params Form data. If you want to post file, please use FormData()
*/
public async put<T>(path: string, params = {}): Promise<Response<T>> {
return axios
.put<T>(this.baseUrl + path, params, {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
let options = {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
}
if (this.proxyConfig) {
options = Object.assign(options, {
httpsAgent: Mastodon._proxyAgent(this.proxyConfig)
})
}
return axios
.put<T>(this.baseUrl + path, params, options)
.catch((err: Error) => {
if (axios.isCancel(err)) {
throw new RequestCanceledError(err.message)
@ -310,13 +360,19 @@ export default class Mastodon implements MegalodonInstance {
* @param params Form data. If you want to post file, please use FormData()
*/
public async patch<T>(path: string, params = {}): Promise<Response<T>> {
return axios
.patch<T>(this.baseUrl + path, params, {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
let options = {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
}
if (this.proxyConfig) {
options = Object.assign(options, {
httpsAgent: Mastodon._proxyAgent(this.proxyConfig)
})
}
return axios
.patch<T>(this.baseUrl + path, params, options)
.catch((err: Error) => {
if (axios.isCancel(err)) {
throw new RequestCanceledError(err.message)
@ -341,22 +397,26 @@ export default class Mastodon implements MegalodonInstance {
* @param params Form data
*/
public async post<T>(path: string, params = {}): Promise<Response<T>> {
return axios
.post<T>(this.baseUrl + path, params, {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
})
.then((resp: AxiosResponse<T>) => {
const res: Response<T> = {
data: resp.data,
status: resp.status,
statusText: resp.statusText,
headers: resp.headers
}
return res
let options = {
cancelToken: this.cancelTokenSource.token,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
}
if (this.proxyConfig) {
options = Object.assign(options, {
httpsAgent: Mastodon._proxyAgent(this.proxyConfig)
})
}
return axios.post<T>(this.baseUrl + path, params, options).then((resp: AxiosResponse<T>) => {
const res: Response<T> = {
data: resp.data,
status: resp.status,
statusText: resp.statusText,
headers: resp.headers
}
return res
})
}
/**
@ -365,14 +425,20 @@ export default class Mastodon implements MegalodonInstance {
* @param params Form data
*/
public async del<T>(path: string, params = {}): Promise<Response<T>> {
return axios
.delete(this.baseUrl + path, {
cancelToken: this.cancelTokenSource.token,
data: params,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
let options = {
cancelToken: this.cancelTokenSource.token,
data: params,
headers: {
Authorization: `Bearer ${this.accessToken}`
}
}
if (this.proxyConfig) {
options = Object.assign(options, {
httpsAgent: Mastodon._proxyAgent(this.proxyConfig)
})
}
return axios
.delete(this.baseUrl + path, options)
.catch((err: Error) => {
if (axios.isCancel(err)) {
throw new RequestCanceledError(err.message)
@ -437,4 +503,13 @@ export default class Mastodon implements MegalodonInstance {
})
return streaming
}
private static _proxyAgent(proxyConfig: ProxyConfig): HttpsProxyAgent {
let auth = ''
if (proxyConfig.auth) {
auth = `${proxyConfig.auth.username}:${proxyConfig.auth.password}@`
}
const agent = new HttpsProxyAgent(`${proxyConfig.protocol}://${auth}${proxyConfig.host}:${proxyConfig.port}`)
return agent
}
}

View file

@ -496,6 +496,13 @@ acorn@^6.0.1, acorn@^6.0.7:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.1.tgz#3ed8422d6dec09e6121cc7a843ca86a330a86b51"
integrity sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==
agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies:
es6-promisify "^5.0.0"
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5:
version "6.10.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
@ -1001,7 +1008,7 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@^3.2.6:
debug@^3.1.0, debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@ -1172,6 +1179,18 @@ es-to-primitive@^1.2.0:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es6-promise@^4.0.3:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
dependencies:
es6-promise "^4.0.3"
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@ -1817,6 +1836,14 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
https-proxy-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz#0106efa5d63d6d6f3ab87c999fa4877a3fd1ff97"
integrity sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==
dependencies:
agent-base "^4.3.0"
debug "^3.1.0"
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"