diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2018-11-07 13:14:52 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2018-11-07 13:14:52 +0900 |
| commit | cb6f390fb6964a032f15c6885d686d07c945ad38 (patch) | |
| tree | e1e5d1de37ec20bc8b49c523fdaeff2227814944 /src/server | |
| parent | Fix (diff) | |
| download | sharkey-cb6f390fb6964a032f15c6885d686d07c945ad38.tar.gz sharkey-cb6f390fb6964a032f15c6885d686d07c945ad38.tar.bz2 sharkey-cb6f390fb6964a032f15c6885d686d07c945ad38.zip | |
GitHub / Twitter連携の設定をDBに保存するように
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/api/endpoints/admin/update-meta.ts | 68 | ||||
| -rw-r--r-- | src/server/api/endpoints/meta.ts | 4 | ||||
| -rw-r--r-- | src/server/api/index.ts | 1 | ||||
| -rw-r--r-- | src/server/api/service/github-bot.ts | 156 | ||||
| -rw-r--r-- | src/server/api/service/github.ts | 480 | ||||
| -rw-r--r-- | src/server/api/service/twitter.ts | 199 |
6 files changed, 496 insertions, 412 deletions
diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index d45a8759f2..39d7ef86b9 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -137,7 +137,49 @@ export const meta = { desc: { 'ja-JP': 'インスタンスの対象言語' } - } + }, + + enableTwitterIntegration: { + validator: $.bool.optional, + desc: { + 'ja-JP': 'Twitter連携機能を有効にするか否か' + } + }, + + twitterConsumerKey: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'TwitterアプリのConsumer key' + } + }, + + twitterConsumerSecret: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'TwitterアプリのConsumer secret' + } + }, + + enableGithubIntegration: { + validator: $.bool.optional, + desc: { + 'ja-JP': 'GitHub連携機能を有効にするか否か' + } + }, + + githubClientId: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'GitHubアプリのClient ID' + } + }, + + githubClientSecret: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'GitHubアプリのClient secret' + } + }, } }; @@ -216,6 +258,30 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { set.langs = ps.langs; } + if (ps.enableTwitterIntegration !== undefined) { + set.enableTwitterIntegration = ps.enableTwitterIntegration; + } + + if (ps.twitterConsumerKey !== undefined) { + set.twitterConsumerKey = ps.twitterConsumerKey; + } + + if (ps.twitterConsumerSecret !== undefined) { + set.twitterConsumerSecret = ps.twitterConsumerSecret; + } + + if (ps.enableGithubIntegration !== undefined) { + set.enableGithubIntegration = ps.enableGithubIntegration; + } + + if (ps.githubClientId !== undefined) { + set.githubClientId = ps.githubClientId; + } + + if (ps.githubClientSecret !== undefined) { + set.githubClientSecret = ps.githubClientSecret; + } + await Meta.update({}, { $set: set }, { upsert: true }); diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 625a9519d7..7cd72d3cc3 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -77,8 +77,8 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { elasticsearch: config.elasticsearch ? true : false, recaptcha: instance.enableRecaptcha, objectStorage: config.drive && config.drive.storage === 'minio', - twitter: config.twitter ? true : false, - github: config.github ? true : false, + twitter: instance.enableTwitterIntegration, + github: instance.enableGithubIntegration, serviceWorker: config.sw ? true : false, userRecommendation: config.user_recommendation ? config.user_recommendation : {} }; diff --git a/src/server/api/index.ts b/src/server/api/index.ts index 33e98f650a..bb8bad8bbe 100644 --- a/src/server/api/index.ts +++ b/src/server/api/index.ts @@ -44,6 +44,7 @@ router.post('/signup', require('./private/signup').default); router.post('/signin', require('./private/signin').default); router.use(require('./service/github').routes()); +router.use(require('./service/github-bot').routes()); router.use(require('./service/twitter').routes()); router.use(require('./mastodon').routes()); diff --git a/src/server/api/service/github-bot.ts b/src/server/api/service/github-bot.ts new file mode 100644 index 0000000000..cb038363f3 --- /dev/null +++ b/src/server/api/service/github-bot.ts @@ -0,0 +1,156 @@ +import * as EventEmitter from 'events'; +import * as Router from 'koa-router'; +import * as request from 'request'; +import User, { IUser } from '../../../models/user'; +import createNote from '../../../services/note/create'; +import config from '../../../config'; +const crypto = require('crypto'); + +const handler = new EventEmitter(); + +let bot: IUser; + +const post = async (text: string, home = true) => { + if (bot == null) { + const account = await User.findOne({ + usernameLower: config.github_bot.username.toLowerCase() + }); + + if (account == null) { + console.warn(`GitHub hook bot specified, but not found: @${config.github_bot.username}`); + return; + } else { + bot = account; + } + } + + createNote(bot, { text, visibility: home ? 'home' : 'public' }); +}; + +// Init router +const router = new Router(); + +if (config.github_bot) { + const secret = config.github_bot.hook_secret; + + router.post('/hooks/github', ctx => { + const body = JSON.stringify(ctx.request.body); + const hash = crypto.createHmac('sha1', secret).update(body).digest('hex'); + const sig1 = new Buffer(ctx.headers['x-hub-signature']); + const sig2 = new Buffer(`sha1=${hash}`); + + // シグネチャ比較 + if (sig1.equals(sig2)) { + handler.emit(ctx.headers['x-github-event'], ctx.request.body); + ctx.status = 204; + } else { + ctx.status = 400; + } + }); +} + +module.exports = router; + +handler.on('status', event => { + const state = event.state; + switch (state) { + case 'error': + case 'failure': + const commit = event.commit; + const parent = commit.parents[0]; + + // Fetch parent status + request({ + url: `${parent.url}/statuses`, + proxy: config.proxy, + headers: { + 'User-Agent': 'misskey' + } + }, (err, res, body) => { + if (err) { + console.error(err); + return; + } + const parentStatuses = JSON.parse(body); + const parentState = parentStatuses[0].state; + const stillFailed = parentState == 'failure' || parentState == 'error'; + if (stillFailed) { + post(`**⚠️BUILD STILL FAILED⚠️**: ?[${commit.commit.message}](${commit.html_url})`); + } else { + post(`**🚨BUILD FAILED🚨**: →→→?[${commit.commit.message}](${commit.html_url})←←←`); + } + }); + break; + } +}); + +handler.on('push', event => { + const ref = event.ref; + switch (ref) { + case 'refs/heads/master': + const pusher = event.pusher; + const compare = event.compare; + const commits: any[] = event.commits; + post([ + `Pushed by **${pusher.name}** with ?[${commits.length} commit${commits.length > 1 ? 's' : ''}](${compare}):`, + commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'), + ].join('\n')); + break; + case 'refs/heads/release': + const commit = event.commits[0]; + post(`RELEASED: ${commit.message}`); + break; + } +}); + +handler.on('issues', event => { + const issue = event.issue; + const action = event.action; + let title: string; + switch (action) { + case 'opened': title = 'Issue opened'; break; + case 'closed': title = 'Issue closed'; break; + case 'reopened': title = 'Issue reopened'; break; + default: return; + } + post(`${title}: <${issue.number}>「${issue.title}」\n${issue.html_url}`); +}); + +handler.on('issue_comment', event => { + const issue = event.issue; + const comment = event.comment; + const action = event.action; + let text: string; + switch (action) { + case 'created': text = `Commented to「${issue.title}」:${comment.user.login}「${comment.body}」\n${comment.html_url}`; break; + default: return; + } + post(text); +}); + +handler.on('watch', event => { + const sender = event.sender; + post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false); +}); + +handler.on('fork', event => { + const repo = event.forkee; + post(`🍴 Forked:\n${repo.html_url} 🍴`); +}); + +handler.on('pull_request', event => { + const pr = event.pull_request; + const action = event.action; + let text: string; + switch (action) { + case 'opened': text = `New Pull Request:「${pr.title}」\n${pr.html_url}`; break; + case 'reopened': text = `Pull Request Reopened:「${pr.title}」\n${pr.html_url}`; break; + case 'closed': + text = pr.merged + ? `Pull Request Merged!:「${pr.title}」\n${pr.html_url}` + : `Pull Request Closed:「${pr.title}」\n${pr.html_url}`; + break; + default: return; + } + post(text); +}); diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index 617bd7d088..4dce856c2a 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -1,37 +1,14 @@ -import * as EventEmitter from 'events'; import * as Koa from 'koa'; import * as Router from 'koa-router'; import * as request from 'request'; import { OAuth2 } from 'oauth'; -import User, { IUser, pack, ILocalUser } from '../../../models/user'; -import createNote from '../../../services/note/create'; +import User, { pack, ILocalUser } from '../../../models/user'; import config from '../../../config'; import { publishMainStream } from '../../../stream'; import redis from '../../../db/redis'; import uuid = require('uuid'); import signin from '../common/signin'; -const crypto = require('crypto'); - -const handler = new EventEmitter(); - -let bot: IUser; - -const post = async (text: string, home = true) => { - if (bot == null) { - const account = await User.findOne({ - usernameLower: config.github_bot.username.toLowerCase() - }); - - if (account == null) { - console.warn(`GitHub hook bot specified, but not found: @${config.github_bot.username}`); - return; - } else { - bot = account; - } - } - - createNote(bot, { text, visibility: home ? 'home' : 'public' }); -}; +import fetchMeta from '../../../misc/fetch-meta'; function getUserToken(ctx: Koa.Context) { return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; @@ -80,337 +57,218 @@ router.get('/disconnect/github', async ctx => { })); }); -if (!config.github || !redis) { - router.get('/connect/github', ctx => { - ctx.body = '現在GitHubへ接続できません (このインスタンスではGitHubはサポートされていません)'; - }); +async function getOath2() { + const meta = await fetchMeta(); - router.get('/signin/github', ctx => { - ctx.body = '現在GitHubへ接続できません (このインスタンスではGitHubはサポートされていません)'; - }); -} else { - const oauth2 = new OAuth2( - config.github.client_id, - config.github.client_secret, - 'https://github.com/', - 'login/oauth/authorize', - 'login/oauth/access_token'); + if (meta.enableGithubIntegration) { + return new OAuth2( + meta.githubClientId, + meta.githubClientSecret, + 'https://github.com/', + 'login/oauth/authorize', + 'login/oauth/access_token'); + } else { + return null; + } +} - router.get('/connect/github', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } +router.get('/connect/github', async ctx => { + if (!compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, 'signin required'); - return; - } + const userToken = getUserToken(ctx); + if (!userToken) { + ctx.throw(400, 'signin required'); + return; + } - const params = { - redirect_uri: `${config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid() - }; + const params = { + redirect_uri: `${config.url}/api/gh/cb`, + scope: ['read:user'], + state: uuid() + }; - redis.set(userToken, JSON.stringify(params)); - ctx.redirect(oauth2.getAuthorizeUrl(params)); - }); + redis.set(userToken, JSON.stringify(params)); - router.get('/signin/github', async ctx => { - const sessid = uuid(); + const oauth2 = await getOath2(); + ctx.redirect(oauth2.getAuthorizeUrl(params)); +}); - const params = { - redirect_uri: `${config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid() - }; +router.get('/signin/github', async ctx => { + const sessid = uuid(); - const expires = 1000 * 60 * 60; // 1h - ctx.cookies.set('signin_with_github_session_id', sessid, { - path: '/', - domain: config.host, - secure: config.url.startsWith('https'), - httpOnly: true, - expires: new Date(Date.now() + expires), - maxAge: expires - }); + const params = { + redirect_uri: `${config.url}/api/gh/cb`, + scope: ['read:user'], + state: uuid() + }; - redis.set(sessid, JSON.stringify(params)); - ctx.redirect(oauth2.getAuthorizeUrl(params)); + const expires = 1000 * 60 * 60; // 1h + ctx.cookies.set('signin_with_github_session_id', sessid, { + path: '/', + domain: config.host, + secure: config.url.startsWith('https'), + httpOnly: true, + expires: new Date(Date.now() + expires), + maxAge: expires }); - router.get('/gh/cb', async ctx => { - const userToken = getUserToken(ctx); + redis.set(sessid, JSON.stringify(params)); - if (!userToken) { - const sessid = ctx.cookies.get('signin_with_github_session_id'); + const oauth2 = await getOath2(); + ctx.redirect(oauth2.getAuthorizeUrl(params)); +}); - if (!sessid) { - ctx.throw(400, 'invalid session'); - return; - } +router.get('/gh/cb', async ctx => { + const userToken = getUserToken(ctx); - const code = ctx.query.code; + const oauth2 = await getOath2(); - if (!code) { - ctx.throw(400, 'invalid session'); - return; - } + if (!userToken) { + const sessid = ctx.cookies.get('signin_with_github_session_id'); - const { redirect_uri, state } = await new Promise<any>((res, rej) => { - redis.get(sessid, async (_, state) => { - res(JSON.parse(state)); - }); - }); + if (!sessid) { + ctx.throw(400, 'invalid session'); + return; + } - if (ctx.query.state !== state) { - ctx.throw(400, 'invalid session'); - return; - } + const code = ctx.query.code; + + if (!code) { + ctx.throw(400, 'invalid session'); + return; + } - const { accessToken } = await new Promise<any>((res, rej) => - oauth2.getOAuthAccessToken( - code, - { redirect_uri }, - (err, accessToken, refresh, result) => { - if (err) - rej(err); - else if (result.error) - rej(result.error); - else - res({ accessToken }); - })); + const { redirect_uri, state } = await new Promise<any>((res, rej) => { + redis.get(sessid, async (_, state) => { + res(JSON.parse(state)); + }); + }); + + if (ctx.query.state !== state) { + ctx.throw(400, 'invalid session'); + return; + } - const { login, id } = await new Promise<any>((res, rej) => - request({ - url: 'https://api.github.com/user', - headers: { - 'Accept': 'application/vnd.github.v3+json', - 'Authorization': `bearer ${accessToken}`, - 'User-Agent': config.user_agent - } - }, (err, response, body) => { + const { accessToken } = await new Promise<any>((res, rej) => + oauth2.getOAuthAccessToken( + code, + { redirect_uri }, + (err, accessToken, refresh, result) => { if (err) rej(err); + else if (result.error) + rej(result.error); else - res(JSON.parse(body)); + res({ accessToken }); })); - if (!login || !id) { - ctx.throw(400, 'invalid session'); - return; - } + const { login, id } = await new Promise<any>((res, rej) => + request({ + url: 'https://api.github.com/user', + headers: { + 'Accept': 'application/vnd.github.v3+json', + 'Authorization': `bearer ${accessToken}`, + 'User-Agent': config.user_agent + } + }, (err, response, body) => { + if (err) + rej(err); + else + res(JSON.parse(body)); + })); + + if (!login || !id) { + ctx.throw(400, 'invalid session'); + return; + } - const user = await User.findOne({ - host: null, - 'github.id': id - }) as ILocalUser; + const user = await User.findOne({ + host: null, + 'github.id': id + }) as ILocalUser; - if (!user) { - ctx.throw(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`); - return; - } + if (!user) { + ctx.throw(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`); + return; + } - signin(ctx, user, true); - } else { - const code = ctx.query.code; + signin(ctx, user, true); + } else { + const code = ctx.query.code; - if (!code) { - ctx.throw(400, 'invalid session'); - return; - } + if (!code) { + ctx.throw(400, 'invalid session'); + return; + } - const { redirect_uri, state } = await new Promise<any>((res, rej) => { - redis.get(userToken, async (_, state) => { - res(JSON.parse(state)); - }); + const { redirect_uri, state } = await new Promise<any>((res, rej) => { + redis.get(userToken, async (_, state) => { + res(JSON.parse(state)); }); + }); - if (ctx.query.state !== state) { - ctx.throw(400, 'invalid session'); - return; - } - - const { accessToken } = await new Promise<any>((res, rej) => - oauth2.getOAuthAccessToken( - code, - { redirect_uri }, - (err, accessToken, refresh, result) => { - if (err) - rej(err); - else if (result.error) - rej(result.error); - else - res({ accessToken }); - })); + if (ctx.query.state !== state) { + ctx.throw(400, 'invalid session'); + return; + } - const { login, id } = await new Promise<any>((res, rej) => - request({ - url: 'https://api.github.com/user', - headers: { - 'Accept': 'application/vnd.github.v3+json', - 'Authorization': `bearer ${accessToken}`, - 'User-Agent': config.user_agent - } - }, (err, response, body) => { + const { accessToken } = await new Promise<any>((res, rej) => + oauth2.getOAuthAccessToken( + code, + { redirect_uri }, + (err, accessToken, refresh, result) => { if (err) rej(err); + else if (result.error) + rej(result.error); else - res(JSON.parse(body)); + res({ accessToken }); })); - if (!login || !id) { - ctx.throw(400, 'invalid session'); - return; - } - - const user = await User.findOneAndUpdate({ - host: null, - token: userToken - }, { - $set: { - github: { - accessToken, - id, - login - } + const { login, id } = await new Promise<any>((res, rej) => + request({ + url: 'https://api.github.com/user', + headers: { + 'Accept': 'application/vnd.github.v3+json', + 'Authorization': `bearer ${accessToken}`, + 'User-Agent': config.user_agent } - }); - - ctx.body = `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream(user._id, 'meUpdated', await pack(user, user, { - detail: true, - includeSecrets: true + }, (err, response, body) => { + if (err) + rej(err); + else + res(JSON.parse(body)); })); - } - }); -} -if (config.github_bot) { - const secret = config.github_bot.hook_secret; - - router.post('/hooks/github', ctx => { - const body = JSON.stringify(ctx.request.body); - const hash = crypto.createHmac('sha1', secret).update(body).digest('hex'); - const sig1 = new Buffer(ctx.headers['x-hub-signature']); - const sig2 = new Buffer(`sha1=${hash}`); - - // シグネチャ比較 - if (sig1.equals(sig2)) { - handler.emit(ctx.headers['x-github-event'], ctx.request.body); - ctx.status = 204; - } else { - ctx.status = 400; + if (!login || !id) { + ctx.throw(400, 'invalid session'); + return; } - }); -} - -module.exports = router; -handler.on('status', event => { - const state = event.state; - switch (state) { - case 'error': - case 'failure': - const commit = event.commit; - const parent = commit.parents[0]; - - // Fetch parent status - request({ - url: `${parent.url}/statuses`, - proxy: config.proxy, - headers: { - 'User-Agent': 'misskey' + const user = await User.findOneAndUpdate({ + host: null, + token: userToken + }, { + $set: { + github: { + accessToken, + id, + login } - }, (err, res, body) => { - if (err) { - console.error(err); - return; - } - const parentStatuses = JSON.parse(body); - const parentState = parentStatuses[0].state; - const stillFailed = parentState == 'failure' || parentState == 'error'; - if (stillFailed) { - post(`**⚠️BUILD STILL FAILED⚠️**: ?[${commit.commit.message}](${commit.html_url})`); - } else { - post(`**🚨BUILD FAILED🚨**: →→→?[${commit.commit.message}](${commit.html_url})←←←`); - } - }); - break; - } -}); - -handler.on('push', event => { - const ref = event.ref; - switch (ref) { - case 'refs/heads/master': - const pusher = event.pusher; - const compare = event.compare; - const commits: any[] = event.commits; - post([ - `Pushed by **${pusher.name}** with ?[${commits.length} commit${commits.length > 1 ? 's' : ''}](${compare}):`, - commits.reverse().map(commit => `・[?[${commit.id.substr(0, 7)}](${commit.url})] ${commit.message.split('\n')[0]}`).join('\n'), - ].join('\n')); - break; - case 'refs/heads/release': - const commit = event.commits[0]; - post(`RELEASED: ${commit.message}`); - break; - } -}); + } + }); -handler.on('issues', event => { - const issue = event.issue; - const action = event.action; - let title: string; - switch (action) { - case 'opened': title = 'Issue opened'; break; - case 'closed': title = 'Issue closed'; break; - case 'reopened': title = 'Issue reopened'; break; - default: return; - } - post(`${title}: <${issue.number}>「${issue.title}」\n${issue.html_url}`); -}); + ctx.body = `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; -handler.on('issue_comment', event => { - const issue = event.issue; - const comment = event.comment; - const action = event.action; - let text: string; - switch (action) { - case 'created': text = `Commented to「${issue.title}」:${comment.user.login}「${comment.body}」\n${comment.html_url}`; break; - default: return; + // Publish i updated event + publishMainStream(user._id, 'meUpdated', await pack(user, user, { + detail: true, + includeSecrets: true + })); } - post(text); }); -handler.on('watch', event => { - const sender = event.sender; - post(`(((⭐️))) Starred by **${sender.login}** (((⭐️)))`, false); -}); - -handler.on('fork', event => { - const repo = event.forkee; - post(`🍴 Forked:\n${repo.html_url} 🍴`); -}); - -handler.on('pull_request', event => { - const pr = event.pull_request; - const action = event.action; - let text: string; - switch (action) { - case 'opened': text = `New Pull Request:「${pr.title}」\n${pr.html_url}`; break; - case 'reopened': text = `Pull Request Reopened:「${pr.title}」\n${pr.html_url}`; break; - case 'closed': - text = pr.merged - ? `Pull Request Merged!:「${pr.title}」\n${pr.html_url}` - : `Pull Request Closed:「${pr.title}」\n${pr.html_url}`; - break; - default: return; - } - post(text); -}); +module.exports = router; diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts index 6c3cdaa138..ced3e8accd 100644 --- a/src/server/api/service/twitter.ts +++ b/src/server/api/service/twitter.ts @@ -7,6 +7,7 @@ import User, { pack, ILocalUser } from '../../../models/user'; import { publishMainStream } from '../../../stream'; import config from '../../../config'; import signin from '../common/signin'; +import fetchMeta from '../../../misc/fetch-meta'; function getUserToken(ctx: Koa.Context) { return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; @@ -55,131 +56,133 @@ router.get('/disconnect/twitter', async ctx => { })); }); -if (config.twitter == null || redis == null) { - router.get('/connect/twitter', ctx => { - ctx.body = '現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)'; - }); - - router.get('/signin/twitter', ctx => { - ctx.body = '現在Twitterへ接続できません (このインスタンスではTwitterはサポートされていません)'; - }); -} else { - const twAuth = autwh({ - consumerKey: config.twitter.consumer_key, - consumerSecret: config.twitter.consumer_secret, - callbackUrl: `${config.url}/api/tw/cb` - }); +async function getTwAuth() { + const meta = await fetchMeta(); - router.get('/connect/twitter', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } + if (meta.enableTwitterIntegration) { + return autwh({ + consumerKey: meta.twitterConsumerKey, + consumerSecret: meta.twitterConsumerSecret, + callbackUrl: `${config.url}/api/tw/cb` + }); + } else { + return null; + } +} - const userToken = getUserToken(ctx); - if (userToken == null) { - ctx.throw(400, 'signin required'); - return; - } +router.get('/connect/twitter', async ctx => { + if (!compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } - const twCtx = await twAuth.begin(); - redis.set(userToken, JSON.stringify(twCtx)); - ctx.redirect(twCtx.url); - }); + const userToken = getUserToken(ctx); + if (userToken == null) { + ctx.throw(400, 'signin required'); + return; + } - router.get('/signin/twitter', async ctx => { - const twCtx = await twAuth.begin(); + const twAuth = await getTwAuth(); + const twCtx = await twAuth.begin(); + redis.set(userToken, JSON.stringify(twCtx)); + ctx.redirect(twCtx.url); +}); - const sessid = uuid(); +router.get('/signin/twitter', async ctx => { + const twAuth = await getTwAuth(); + const twCtx = await twAuth.begin(); - redis.set(sessid, JSON.stringify(twCtx)); + const sessid = uuid(); - const expires = 1000 * 60 * 60; // 1h - ctx.cookies.set('signin_with_twitter_session_id', sessid, { - path: '/', - domain: config.host, - secure: config.url.startsWith('https'), - httpOnly: true, - expires: new Date(Date.now() + expires), - maxAge: expires - }); + redis.set(sessid, JSON.stringify(twCtx)); - ctx.redirect(twCtx.url); + const expires = 1000 * 60 * 60; // 1h + ctx.cookies.set('signin_with_twitter_session_id', sessid, { + path: '/', + domain: config.host, + secure: config.url.startsWith('https'), + httpOnly: true, + expires: new Date(Date.now() + expires), + maxAge: expires }); - router.get('/tw/cb', async ctx => { - const userToken = getUserToken(ctx); + ctx.redirect(twCtx.url); +}); - if (userToken == null) { - const sessid = ctx.cookies.get('signin_with_twitter_session_id'); +router.get('/tw/cb', async ctx => { + const userToken = getUserToken(ctx); - if (sessid == null) { - ctx.throw(400, 'invalid session'); - return; - } + const twAuth = await getTwAuth(); + + if (userToken == null) { + const sessid = ctx.cookies.get('signin_with_twitter_session_id'); + + if (sessid == null) { + ctx.throw(400, 'invalid session'); + return; + } - const get = new Promise<any>((res, rej) => { - redis.get(sessid, async (_, twCtx) => { - res(twCtx); - }); + const get = new Promise<any>((res, rej) => { + redis.get(sessid, async (_, twCtx) => { + res(twCtx); }); + }); - const twCtx = await get; + const twCtx = await get; - const result = await twAuth.done(JSON.parse(twCtx), ctx.query.oauth_verifier); + const result = await twAuth.done(JSON.parse(twCtx), ctx.query.oauth_verifier); - const user = await User.findOne({ - host: null, - 'twitter.userId': result.userId - }) as ILocalUser; + const user = await User.findOne({ + host: null, + 'twitter.userId': result.userId + }) as ILocalUser; - if (user == null) { - ctx.throw(404, `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); - return; - } + if (user == null) { + ctx.throw(404, `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); + return; + } - signin(ctx, user, true); - } else { - const verifier = ctx.query.oauth_verifier; + signin(ctx, user, true); + } else { + const verifier = ctx.query.oauth_verifier; - if (verifier == null) { - ctx.throw(400, 'invalid session'); - return; - } + if (verifier == null) { + ctx.throw(400, 'invalid session'); + return; + } - const get = new Promise<any>((res, rej) => { - redis.get(userToken, async (_, twCtx) => { - res(twCtx); - }); + const get = new Promise<any>((res, rej) => { + redis.get(userToken, async (_, twCtx) => { + res(twCtx); }); + }); - const twCtx = await get; + const twCtx = await get; - const result = await twAuth.done(JSON.parse(twCtx), verifier); + const result = await twAuth.done(JSON.parse(twCtx), verifier); - const user = await User.findOneAndUpdate({ - host: null, - token: userToken - }, { - $set: { - twitter: { - accessToken: result.accessToken, - accessTokenSecret: result.accessTokenSecret, - userId: result.userId, - screenName: result.screenName - } + const user = await User.findOneAndUpdate({ + host: null, + token: userToken + }, { + $set: { + twitter: { + accessToken: result.accessToken, + accessTokenSecret: result.accessTokenSecret, + userId: result.userId, + screenName: result.screenName } - }); + } + }); - ctx.body = `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; + ctx.body = `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; - // Publish i updated event - publishMainStream(user._id, 'meUpdated', await pack(user, user, { - detail: true, - includeSecrets: true - })); - } - }); -} + // Publish i updated event + publishMainStream(user._id, 'meUpdated', await pack(user, user, { + detail: true, + includeSecrets: true + })); + } +}); module.exports = router; |