parent
							
								
									c146006476
								
							
						
					
					
						commit
						147ad69864
					
				
					 14 changed files with 8 additions and 416 deletions
				
			
		| 
						 | 
				
			
			@ -73,7 +73,6 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 | 
			
		|||
			recaptcha: config.recaptcha ? true : false,
 | 
			
		||||
			objectStorage: config.drive && config.drive.storage === 'minio',
 | 
			
		||||
			twitter: config.twitter ? true : false,
 | 
			
		||||
			github: config.github ? true : false,
 | 
			
		||||
			serviceWorker: config.sw ? true : false,
 | 
			
		||||
			userRecommendation: config.user_recommendation ? config.user_recommendation : {}
 | 
			
		||||
		} : undefined
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,11 @@
 | 
			
		|||
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';
 | 
			
		||||
const crypto = require('crypto');
 | 
			
		||||
 | 
			
		||||
import User, { IUser } from '../../../models/user';
 | 
			
		||||
import createNote from '../../../services/note/create';
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,264 +28,10 @@ const post = async (text: string, home = true) => {
 | 
			
		|||
	createNote(bot, { text, visibility: home ? 'home' : 'public' });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function getUserToken(ctx: Koa.Context) {
 | 
			
		||||
	return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function compareOrigin(ctx: Koa.Context) {
 | 
			
		||||
	function normalizeUrl(url: string) {
 | 
			
		||||
		return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : '';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const referer = ctx.headers['referer'];
 | 
			
		||||
 | 
			
		||||
	return (normalizeUrl(referer) == normalizeUrl(config.url));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init router
 | 
			
		||||
const router = new Router();
 | 
			
		||||
 | 
			
		||||
router.get('/disconnect/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 user = await User.findOneAndUpdate({
 | 
			
		||||
		host: null,
 | 
			
		||||
		'token': userToken
 | 
			
		||||
	}, {
 | 
			
		||||
		$set: {
 | 
			
		||||
			'github': null
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	ctx.body = `GitHubの連携を解除しました :v:`;
 | 
			
		||||
 | 
			
		||||
	// Publish i updated event
 | 
			
		||||
	publishMainStream(user._id, 'meUpdated', await pack(user, user, {
 | 
			
		||||
		detail: true,
 | 
			
		||||
		includeSecrets: true
 | 
			
		||||
	}));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
if (!config.github || !redis) {
 | 
			
		||||
	router.get('/connect/github', ctx => {
 | 
			
		||||
		ctx.body = '現在GitHubへ接続できません (このインスタンスではGitHubはサポートされていません)';
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	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');
 | 
			
		||||
 | 
			
		||||
	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 params = {
 | 
			
		||||
			redirect_uri: `${config.url}:8089/api/gh/cb`,
 | 
			
		||||
			scope: ['read:user'],
 | 
			
		||||
			state: uuid()
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		redis.set(userToken, JSON.stringify(params));
 | 
			
		||||
		ctx.redirect(oauth2.getAuthorizeUrl(params));
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	router.get('/signin/github', async ctx => {
 | 
			
		||||
		const sessid = uuid();
 | 
			
		||||
 | 
			
		||||
		const params = {
 | 
			
		||||
			redirect_uri: `${config.url}:8089/api/gh/cb`,
 | 
			
		||||
			scope: ['read:user'],
 | 
			
		||||
			state: 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
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		redis.set(sessid, JSON.stringify(params));
 | 
			
		||||
		ctx.redirect(oauth2.getAuthorizeUrl(params));
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	router.get('/gh/cb', async ctx => {
 | 
			
		||||
		const userToken = getUserToken(ctx);
 | 
			
		||||
 | 
			
		||||
		if (!userToken) {
 | 
			
		||||
			const sessid = ctx.cookies.get('signin_with_github_session_id');
 | 
			
		||||
 | 
			
		||||
			if (!sessid) {
 | 
			
		||||
				ctx.throw(400, 'invalid session');
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const code = ctx.query.code;
 | 
			
		||||
 | 
			
		||||
			if (!code) {
 | 
			
		||||
				ctx.throw(400, 'invalid session');
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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 { 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 { 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;
 | 
			
		||||
 | 
			
		||||
			if (!user) {
 | 
			
		||||
				ctx.throw(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			signin(ctx, user, true);
 | 
			
		||||
		} else {
 | 
			
		||||
			const code = ctx.query.code;
 | 
			
		||||
 | 
			
		||||
			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));
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			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 });
 | 
			
		||||
					}));
 | 
			
		||||
 | 
			
		||||
			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.findOneAndUpdate({
 | 
			
		||||
				host: null,
 | 
			
		||||
				token: userToken
 | 
			
		||||
			}, {
 | 
			
		||||
				$set: {
 | 
			
		||||
					github: {
 | 
			
		||||
						accessToken,
 | 
			
		||||
						id,
 | 
			
		||||
						login
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			ctx.body = `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`;
 | 
			
		||||
 | 
			
		||||
			// Publish i updated event
 | 
			
		||||
			publishMainStream(user._id, 'meUpdated', await pack(user, user, {
 | 
			
		||||
				detail: true,
 | 
			
		||||
				includeSecrets: true
 | 
			
		||||
			}));
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (config.github_bot) {
 | 
			
		||||
if (config.github_bot != null) {
 | 
			
		||||
	const secret = config.github_bot.hook_secret;
 | 
			
		||||
 | 
			
		||||
	router.post('/hooks/github', ctx => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue