refs #982 Add proxy configuration for all requests from main

This commit is contained in:
AkiraFukushima 2019-10-27 17:22:44 +09:00
parent 296ec5c6c2
commit a47fbbcf6a
6 changed files with 81 additions and 37 deletions

View File

@ -1,5 +1,5 @@
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import Mastodon, { Account as RemoteAccount } from 'megalodon' import Mastodon, { Account as RemoteAccount, ProxyConfig } from 'megalodon'
import Datastore from 'nedb' import Datastore from 'nedb'
import log from 'electron-log' import log from 'electron-log'
import { LocalAccount } from '~/src/types/localAccount' import { LocalAccount } from '~/src/types/localAccount'
@ -275,14 +275,14 @@ export default class Account {
return updated return updated
} }
async refreshAccounts(): Promise<Array<LocalAccount>> { async refreshAccounts(proxy: ProxyConfig | false): Promise<Array<LocalAccount>> {
const accounts = await this.listAccounts() const accounts = await this.listAccounts()
if (accounts.length < 1) { if (accounts.length < 1) {
return accounts return accounts
} }
const results = await Promise.all( const results = await Promise.all(
accounts.map(async account => { accounts.map(async account => {
const refresh = await this.refresh(account) const refresh = await this.refresh(account, proxy)
return refresh return refresh
}) })
) )
@ -294,8 +294,8 @@ export default class Account {
* @param {LocalAccount} account is an local account * @param {LocalAccount} account is an local account
* @return {LocalAccount} updated account * @return {LocalAccount} updated account
*/ */
async refresh(account: LocalAccount): Promise<LocalAccount> { async refresh(account: LocalAccount, proxy: ProxyConfig | false): Promise<LocalAccount> {
let client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1') let client = new Mastodon(account.accessToken!, account.baseURL + '/api/v1', 'Whalebird', proxy)
let json = {} let json = {}
try { try {
const res = await client.get<RemoteAccount>('/accounts/verify_credentials') const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
@ -311,8 +311,8 @@ export default class Account {
if (!account.refreshToken) { if (!account.refreshToken) {
throw new RefreshTokenDoesNotExist() throw new RefreshTokenDoesNotExist()
} }
const token = await Mastodon.refreshToken(account.clientId, account.clientSecret, account.refreshToken, account.baseURL) const token = await Mastodon.refreshToken(account.clientId, account.clientSecret, account.refreshToken, account.baseURL, proxy)
client = new Mastodon(token.access_token, account.baseURL + '/api/v1') client = new Mastodon(token.access_token, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res = await client.get<RemoteAccount>('/accounts/verify_credentials') const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
json = { json = {
username: res.data.username, username: res.data.username,
@ -326,8 +326,8 @@ export default class Account {
} }
// Confirm the access token, and check duplicate // Confirm the access token, and check duplicate
async fetchAccount(account: LocalAccount, accessToken: string): Promise<RemoteAccount> { async fetchAccount(account: LocalAccount, accessToken: string, proxy: ProxyConfig | false): Promise<RemoteAccount> {
const client = new Mastodon(accessToken, account.baseURL + '/api/v1') const client = new Mastodon(accessToken, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res = await client.get<RemoteAccount>('/accounts/verify_credentials') const res = await client.get<RemoteAccount>('/accounts/verify_credentials')
const query = { const query = {
baseURL: account.baseURL, baseURL: account.baseURL,

View File

@ -1,4 +1,4 @@
import Mastodon, { OAuth } from 'megalodon' import Mastodon, { OAuth, ProxyConfig } from 'megalodon'
import Account from './account' import Account from './account'
import { LocalAccount } from '~/src/types/localAccount' import { LocalAccount } from '~/src/types/localAccount'
@ -30,7 +30,7 @@ export default class Authentication {
this.clientSecret = '' this.clientSecret = ''
} }
async getAuthorizationUrl(domain = 'mastodon.social'): Promise<string> { async getAuthorizationUrl(domain = 'mastodon.social', proxy: ProxyConfig | false): Promise<string> {
this.setOtherInstance(domain) this.setOtherInstance(domain)
const res = await Mastodon.registerApp( const res = await Mastodon.registerApp(
appName, appName,
@ -38,7 +38,8 @@ export default class Authentication {
scopes: scope, scopes: scope,
website: appURL website: appURL
}, },
this.baseURL this.baseURL,
proxy
) )
this.clientId = res.clientId this.clientId = res.clientId
this.clientSecret = res.clientSecret this.clientSecret = res.clientSecret
@ -69,8 +70,15 @@ export default class Authentication {
return res.url return res.url
} }
async getAccessToken(code: string): Promise<string> { async getAccessToken(code: string, proxy: ProxyConfig | false): Promise<string> {
const tokenData: OAuth.TokenData = await Mastodon.fetchAccessToken(this.clientId, this.clientSecret, code, this.baseURL) const tokenData: OAuth.TokenData = await Mastodon.fetchAccessToken(
this.clientId,
this.clientSecret,
code,
this.baseURL,
'urn:ietf:wg:oauth:2.0:oob',
proxy
)
const search = { const search = {
baseURL: this.baseURL, baseURL: this.baseURL,
domain: this.domain, domain: this.domain,
@ -80,7 +88,7 @@ export default class Authentication {
const rec = await this.db.searchAccount(search) const rec = await this.db.searchAccount(search)
const accessToken = tokenData.accessToken const accessToken = tokenData.accessToken
const refreshToken = tokenData.refreshToken const refreshToken = tokenData.refreshToken
const data = await this.db.fetchAccount(rec, accessToken) const data = await this.db.fetchAccount(rec, accessToken, proxy)
await this.db.updateAccount(rec._id!, { await this.db.updateAccount(rec._id!, {
username: data.username, username: data.username,
accountId: data.id, accountId: data.id,

View File

@ -349,9 +349,10 @@ app.on('activate', () => {
let auth = new Authentication(accountManager) let auth = new Authentication(accountManager)
ipcMain.on('get-auth-url', (event: IpcMainEvent, domain: string) => { ipcMain.on('get-auth-url', async (event: IpcMainEvent, domain: string) => {
const proxy = await proxyConfiguration.forMastodon()
auth auth
.getAuthorizationUrl(domain) .getAuthorizationUrl(domain, proxy)
.then(url => { .then(url => {
log.debug(url) log.debug(url)
event.sender.send('response-get-auth-url', url) event.sender.send('response-get-auth-url', url)
@ -364,9 +365,10 @@ ipcMain.on('get-auth-url', (event: IpcMainEvent, domain: string) => {
}) })
}) })
ipcMain.on('get-access-token', (event: IpcMainEvent, code: string) => { ipcMain.on('get-access-token', async (event: IpcMainEvent, code: string) => {
const proxy = await proxyConfiguration.forMastodon()
auth auth
.getAccessToken(code) .getAccessToken(code, proxy)
.then(token => { .then(token => {
accountDB.findOne( accountDB.findOne(
{ {
@ -419,9 +421,10 @@ ipcMain.on('get-local-account', (event: IpcMainEvent, id: string) => {
}) })
}) })
ipcMain.on('update-account', (event: IpcMainEvent, acct: LocalAccount) => { ipcMain.on('update-account', async (event: IpcMainEvent, acct: LocalAccount) => {
const proxy = await proxyConfiguration.forMastodon()
accountManager accountManager
.refresh(acct) .refresh(acct, proxy)
.then(ac => { .then(ac => {
event.sender.send('response-update-account', ac) event.sender.send('response-update-account', ac)
}) })
@ -465,9 +468,10 @@ ipcMain.on('backward-account', (event: IpcMainEvent, acct: LocalAccount) => {
}) })
}) })
ipcMain.on('refresh-accounts', (event: IpcMainEvent) => { ipcMain.on('refresh-accounts', async (event: IpcMainEvent) => {
const proxy = await proxyConfiguration.forMastodon()
accountManager accountManager
.refreshAccounts() .refreshAccounts(proxy)
.then(accounts => { .then(accounts => {
event.sender.send('response-refresh-accounts', accounts) event.sender.send('response-refresh-accounts', accounts)
}) })
@ -523,7 +527,8 @@ ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<Lo
userStreamings[id]!.stop() userStreamings[id]!.stop()
userStreamings[id] = null userStreamings[id] = null
} }
const url = await StreamingURL(acct) const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct, proxy)
userStreamings[id] = new WebSocket(acct, url) userStreamings[id] = new WebSocket(acct, url)
userStreamings[id]!.startUserStreaming( userStreamings[id]!.startUserStreaming(
async (update: Status) => { async (update: Status) => {
@ -633,8 +638,8 @@ ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, obj: St
directMessagesStreaming.stop() directMessagesStreaming.stop()
directMessagesStreaming = null directMessagesStreaming = null
} }
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct) const url = await StreamingURL(acct, proxy)
directMessagesStreaming = new WebSocket(acct, url) directMessagesStreaming = new WebSocket(acct, url)
directMessagesStreaming.start( directMessagesStreaming.start(
'direct', 'direct',
@ -682,8 +687,8 @@ ipcMain.on('start-local-streaming', async (event: IpcMainEvent, obj: StreamingSe
localStreaming.stop() localStreaming.stop()
localStreaming = null localStreaming = null
} }
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct) const url = await StreamingURL(acct, proxy)
localStreaming = new WebSocket(acct, url) localStreaming = new WebSocket(acct, url)
localStreaming.start( localStreaming.start(
'public:local', 'public:local',
@ -731,8 +736,8 @@ ipcMain.on('start-public-streaming', async (event: IpcMainEvent, obj: StreamingS
publicStreaming.stop() publicStreaming.stop()
publicStreaming = null publicStreaming = null
} }
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct) const url = await StreamingURL(acct, proxy)
publicStreaming = new WebSocket(acct, url) publicStreaming = new WebSocket(acct, url)
publicStreaming.start( publicStreaming.start(
'public', 'public',
@ -784,8 +789,8 @@ ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListID & Str
listStreaming.stop() listStreaming.stop()
listStreaming = null listStreaming = null
} }
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct) const url = await StreamingURL(acct, proxy)
listStreaming = new WebSocket(acct, url) listStreaming = new WebSocket(acct, url)
listStreaming.start( listStreaming.start(
`list&list=${listID}`, `list&list=${listID}`,
@ -837,8 +842,8 @@ ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: Tag & Streami
tagStreaming.stop() tagStreaming.stop()
tagStreaming = null tagStreaming = null
} }
const proxy = await proxyConfiguration.forMastodon()
const url = await StreamingURL(acct) const url = await StreamingURL(acct, proxy)
tagStreaming = new WebSocket(acct, url) tagStreaming = new WebSocket(acct, url)
tagStreaming.start( tagStreaming.start(
`hashtag&tag=${tag}`, `hashtag&tag=${tag}`,

View File

@ -1,3 +1,4 @@
import { ProxyConfig } from 'megalodon'
import { ProxySource, ManualProxy, ProxyProtocol } from '~/src/types/proxy' import { ProxySource, ManualProxy, ProxyProtocol } from '~/src/types/proxy'
import Preferences from './preferences' import Preferences from './preferences'
@ -13,6 +14,35 @@ export default class ProxyConfiguration {
this.systemProxy = proxy this.systemProxy = proxy
} }
public async forMastodon(): Promise<false | ProxyConfig> {
const proxy = await this.getConfig()
if (!proxy) {
return false
} else {
let protocol = ProxyProtocol.http
if (proxy.protocol !== '') {
protocol = proxy.protocol
}
if (proxy.username.length > 0) {
return {
host: proxy.host,
port: parseInt(proxy.port, 10),
protocol: protocol,
auth: {
username: proxy.username,
password: proxy.password
}
}
} else {
return {
host: proxy.host,
port: parseInt(proxy.port, 10),
protocol: protocol
}
}
}
}
public async getConfig(): Promise<false | ManualProxy> { public async getConfig(): Promise<false | ManualProxy> {
const conf = await this.preferences.get() const conf = await this.preferences.get()
const source = conf.proxy.source as ProxySource const source = conf.proxy.source as ProxySource

View File

@ -1,12 +1,12 @@
import Mastodon, { WebSocket as SocketListener, Status, Notification, Instance, Response } from 'megalodon' import Mastodon, { WebSocket as SocketListener, Status, Notification, Instance, Response, ProxyConfig } from 'megalodon'
import log from 'electron-log' import log from 'electron-log'
import { LocalAccount } from '~/src/types/localAccount' import { LocalAccount } from '~/src/types/localAccount'
const StreamingURL = async (account: LocalAccount): Promise<string> => { const StreamingURL = async (account: LocalAccount, proxy: ProxyConfig | false): Promise<string> => {
if (!account.accessToken) { if (!account.accessToken) {
throw new Error('access token is empty') throw new Error('access token is empty')
} }
const client = new Mastodon(account.accessToken, account.baseURL + '/api/v1') const client = new Mastodon(account.accessToken, account.baseURL + '/api/v1', 'Whalebird', proxy)
const res: Response<Instance> = await client.get<Instance>('/instance') const res: Response<Instance> = await client.get<Instance>('/instance')
return res.data.urls.streaming_api return res.data.urls.streaming_api
} }

View File

@ -102,6 +102,7 @@ const mutations: MutationTree<AppState> = {
state.hideAllAttachments = hideAllAttachments state.hideAllAttachments = hideAllAttachments
}, },
[MUTATION_TYPES.UPDATE_PROXY_CONFIGURATION]: (state, proxy: ManualProxy | false) => { [MUTATION_TYPES.UPDATE_PROXY_CONFIGURATION]: (state, proxy: ManualProxy | false) => {
// TODO: use src/main/proxy.ts#forMastodon
if (!proxy) { if (!proxy) {
state.proxyConfiguration = false state.proxyConfiguration = false
} else { } else {