parent
							
								
									c146006476
								
							
						
					
					
						commit
						147ad69864
					
				
					 14 changed files with 8 additions and 416 deletions
				
			
		| 
						 | 
				
			
			@ -148,12 +148,6 @@ drive:
 | 
			
		|||
#  consumer_key: example-twitter-consumer-key
 | 
			
		||||
#  consumer_secret: example-twitter-consumer-secret-key
 | 
			
		||||
 | 
			
		||||
# GitHub integration
 | 
			
		||||
# You need to set the oauth callback url as : https://<your-misskey-instance>/api/gh/cb
 | 
			
		||||
#github:
 | 
			
		||||
#  client_id: example-github-client-id
 | 
			
		||||
#  client_secret: example-github-client-secret
 | 
			
		||||
 | 
			
		||||
# Ghost
 | 
			
		||||
# Ghost account is an account used for the purpose of delegating
 | 
			
		||||
# followers when putting users in the list.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -417,7 +417,6 @@ common/views/components/signin.vue:
 | 
			
		|||
  signin: "サインイン"
 | 
			
		||||
  or: "または"
 | 
			
		||||
  signin-with-twitter: "Twitterでログイン"
 | 
			
		||||
  signin-with-github: "GitHubでログイン"
 | 
			
		||||
  login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
 | 
			
		||||
 | 
			
		||||
common/views/components/signup.vue:
 | 
			
		||||
| 
						 | 
				
			
			@ -461,14 +460,6 @@ common/views/components/twitter-setting.vue:
 | 
			
		|||
  connect: "Twitterと接続する"
 | 
			
		||||
  disconnect: "切断する"
 | 
			
		||||
 | 
			
		||||
common/views/components/github-setting.vue:
 | 
			
		||||
  description: "お使いのGitHubアカウントをお使いのMisskeyアカウントに接続しておくと、プロフィールでGitHubアカウント情報が表示されるようになったり、GitHubを用いた便利なサインインを利用できるようになります。"
 | 
			
		||||
  connected-to: "次のGitHubアカウントに接続されています"
 | 
			
		||||
  detail: "詳細..."
 | 
			
		||||
  reconnect: "再接続する"
 | 
			
		||||
  connect: "GitHubと接続する"
 | 
			
		||||
  disconnect: "切断する"
 | 
			
		||||
 | 
			
		||||
common/views/components/uploader.vue:
 | 
			
		||||
  waiting: "待機中"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1569,10 +1560,6 @@ mobile/views/pages/settings.vue:
 | 
			
		|||
  twitter-connect: "Twitterアカウントに接続する"
 | 
			
		||||
  twitter-reconnect: "再接続する"
 | 
			
		||||
  twitter-disconnect: "切断する"
 | 
			
		||||
  github: "GitHub連携"
 | 
			
		||||
  github-connect: "GitHubアカウントに接続する"
 | 
			
		||||
  github-reconnect: "再接続する"
 | 
			
		||||
  github-disconnect: "切断する"
 | 
			
		||||
  update: "Misskey Update"
 | 
			
		||||
  version: "バージョン:"
 | 
			
		||||
  latest-version: "最新のバージョン:"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,7 +62,6 @@
 | 
			
		|||
		"@types/mongodb": "3.1.12",
 | 
			
		||||
		"@types/ms": "0.7.30",
 | 
			
		||||
		"@types/node": "10.12.2",
 | 
			
		||||
		"@types/oauth": "0.9.1",
 | 
			
		||||
		"@types/portscanner": "2.1.0",
 | 
			
		||||
		"@types/pug": "2.0.4",
 | 
			
		||||
		"@types/qrcode": "1.3.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,63 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="mk-github-setting">
 | 
			
		||||
	<p>%i18n:@description%<a :href="`${docsUrl}/link-to-github`" target="_blank">%i18n:@detail%</a></p>
 | 
			
		||||
	<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">%i18n:@connected-to%: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
 | 
			
		||||
	<p>
 | 
			
		||||
		<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a>
 | 
			
		||||
		<span v-if="$store.state.i.github"> or </span>
 | 
			
		||||
		<a :href="`${apiUrl}/disconnect/github`" target="_blank" v-if="$store.state.i.github" @click.prevent="disconnect">%i18n:@disconnect%</a>
 | 
			
		||||
	</p>
 | 
			
		||||
	<p class="id" v-if="$store.state.i.github">GitHub ID: {{ $store.state.i.github.id }}</p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { apiUrl, docsUrl } from '../../../config';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			form: null,
 | 
			
		||||
			apiUrl,
 | 
			
		||||
			docsUrl
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$watch('$store.state.i', () => {
 | 
			
		||||
			if (this.$store.state.i.github && this.form)
 | 
			
		||||
				this.form.close();
 | 
			
		||||
		}, {
 | 
			
		||||
			deep: true
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		connect() {
 | 
			
		||||
			this.form = window.open(apiUrl + '/connect/github',
 | 
			
		||||
				'github_connect_window',
 | 
			
		||||
				'height=570, width=520');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disconnect() {
 | 
			
		||||
			window.open(apiUrl + '/disconnect/github',
 | 
			
		||||
				'github_disconnect_window',
 | 
			
		||||
				'height=570, width=520');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-github-setting
 | 
			
		||||
	.account
 | 
			
		||||
		border solid 1px #e1e8ed
 | 
			
		||||
		border-radius 4px
 | 
			
		||||
		padding 16px
 | 
			
		||||
 | 
			
		||||
		a
 | 
			
		||||
			font-weight bold
 | 
			
		||||
			color inherit
 | 
			
		||||
 | 
			
		||||
	.id
 | 
			
		||||
		color #8899a6
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,6 @@ import messaging from './messaging.vue';
 | 
			
		|||
import messagingRoom from './messaging-room.vue';
 | 
			
		||||
import urlPreview from './url-preview.vue';
 | 
			
		||||
import twitterSetting from './twitter-setting.vue';
 | 
			
		||||
import githubSetting from './github-setting.vue';
 | 
			
		||||
import fileTypeIcon from './file-type-icon.vue';
 | 
			
		||||
import Reversi from './games/reversi/reversi.vue';
 | 
			
		||||
import welcomeTimeline from './welcome-timeline.vue';
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +90,6 @@ Vue.component('mk-messaging', messaging);
 | 
			
		|||
Vue.component('mk-messaging-room', messagingRoom);
 | 
			
		||||
Vue.component('mk-url-preview', urlPreview);
 | 
			
		||||
Vue.component('mk-twitter-setting', twitterSetting);
 | 
			
		||||
Vue.component('mk-github-setting', githubSetting);
 | 
			
		||||
Vue.component('mk-file-type-icon', fileTypeIcon);
 | 
			
		||||
Vue.component('mk-reversi', Reversi);
 | 
			
		||||
Vue.component('mk-welcome-timeline', welcomeTimeline);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,6 @@
 | 
			
		|||
	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill"/>
 | 
			
		||||
	<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
 | 
			
		||||
	<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
 | 
			
		||||
	<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/github`">%i18n:@signin-with-github%</a></p>
 | 
			
		||||
</form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,13 +23,6 @@
 | 
			
		|||
					<mk-twitter-setting/>
 | 
			
		||||
				</section>
 | 
			
		||||
			</ui-card>
 | 
			
		||||
 | 
			
		||||
			<ui-card>
 | 
			
		||||
				<div slot="title">%fa:B github% %i18n:@github%</div>
 | 
			
		||||
				<section>
 | 
			
		||||
					<mk-github-setting/>
 | 
			
		||||
				</section>
 | 
			
		||||
			</ui-card>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<ui-card class="theme" v-show="page == 'theme'">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,27 +0,0 @@
 | 
			
		|||
<template>
 | 
			
		||||
<div class="aqooishiizumijmihokohinatamihoaz">
 | 
			
		||||
	<span>%fa:B github%<a :href="`https://github.com/${user.github.login}`" target="_blank">@{{ user.github.login }}</a></span>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: ['user']
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.aqooishiizumijmihokohinatamihoaz
 | 
			
		||||
.adsvaidqfznoartcbplullnejvxjphcn
 | 
			
		||||
	padding 32px
 | 
			
		||||
	background #171515
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
	color #fff
 | 
			
		||||
 | 
			
		||||
	a
 | 
			
		||||
		margin-left 8px
 | 
			
		||||
		color #fff
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
<mk-ui>
 | 
			
		||||
	<div class="xygkxeaeontfaokvqmiblezmhvhostak" v-if="!fetching">
 | 
			
		||||
		<div class="is-suspended" v-if="user.isSuspended">%fa:exclamation-triangle% %i18n:@is-suspended%</div>
 | 
			
		||||
		<div class="is-remote" v-if="user.host">%fa:exclamation-triangle% %i18n:common.is-remote-user%<a :href="user.url || user.uri" target="_blank">%i18n:common.view-on-remote%</a></div>
 | 
			
		||||
		<div class="is-remote" v-if="user.host != null">%fa:exclamation-triangle% %i18n:common.is-remote-user%<a :href="user.url || user.uri" target="_blank">%i18n:common.view-on-remote%</a></div>
 | 
			
		||||
		<main>
 | 
			
		||||
			<div class="main">
 | 
			
		||||
				<x-header :user="user"/>
 | 
			
		||||
| 
						 | 
				
			
			@ -12,15 +12,14 @@
 | 
			
		|||
			<div class="side">
 | 
			
		||||
				<div class="instance" v-if="!$store.getters.isSignedIn"><mk-instance/></div>
 | 
			
		||||
				<x-profile :user="user"/>
 | 
			
		||||
				<x-twitter :user="user" v-if="!user.host && user.twitter"/>
 | 
			
		||||
				<x-github :user="user" v-if="!user.host && user.github"/>
 | 
			
		||||
				<x-twitter :user="user" v-if="user.host === null && user.twitter"/>
 | 
			
		||||
				<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>
 | 
			
		||||
				<mk-activity :user="user"/>
 | 
			
		||||
				<x-photos :user="user"/>
 | 
			
		||||
				<x-friends :user="user"/>
 | 
			
		||||
				<x-followers-you-know v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
 | 
			
		||||
				<div class="nav"><mk-nav/></div>
 | 
			
		||||
				<p v-if="!user.host">%i18n:@last-used-at%: <b><mk-time :time="user.lastUsedAt"/></b></p>
 | 
			
		||||
				<p v-if="user.host === null">%i18n:@last-used-at%: <b><mk-time :time="user.lastUsedAt"/></b></p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</main>
 | 
			
		||||
	</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +37,6 @@ import XPhotos from './user.photos.vue';
 | 
			
		|||
import XFollowersYouKnow from './user.followers-you-know.vue';
 | 
			
		||||
import XFriends from './user.friends.vue';
 | 
			
		||||
import XTwitter from './user.twitter.vue';
 | 
			
		||||
import XGithub from './user.github.vue'; // ?MEM: Don't fix the intentional typo. (XGitHub -> `<x-git-hub>`)
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,8 +46,7 @@ export default Vue.extend({
 | 
			
		|||
		XPhotos,
 | 
			
		||||
		XFollowersYouKnow,
 | 
			
		||||
		XFriends,
 | 
			
		||||
		XTwitter,
 | 
			
		||||
		XGithub // ?MEM: Don't fix the intentional typo. (see L41)
 | 
			
		||||
		XTwitter
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,19 +125,6 @@
 | 
			
		|||
				</section>
 | 
			
		||||
			</ui-card>
 | 
			
		||||
 | 
			
		||||
			<ui-card>
 | 
			
		||||
				<div slot="title">%fa:B github% %i18n:@github%</div>
 | 
			
		||||
 | 
			
		||||
				<section>
 | 
			
		||||
					<p class="account" v-if="$store.state.i.github"><a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
 | 
			
		||||
					<p>
 | 
			
		||||
						<a :href="`${apiUrl}/connect/github`" target="_blank">{{ $store.state.i.github ? '%i18n:@github-reconnect%' : '%i18n:@github-connect%' }}</a>
 | 
			
		||||
						<span v-if="$store.state.i.github"> or </span>
 | 
			
		||||
						<a :href="`${apiUrl}/disconnect/github`" target="_blank" v-if="$store.state.i.github">%i18n:@github-disconnect%</a>
 | 
			
		||||
					</p>
 | 
			
		||||
				</section>
 | 
			
		||||
			</ui-card>
 | 
			
		||||
 | 
			
		||||
			<mk-api-settings />
 | 
			
		||||
 | 
			
		||||
			<ui-card>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,10 +74,6 @@ export type Source = {
 | 
			
		|||
		consumer_key: string;
 | 
			
		||||
		consumer_secret: string;
 | 
			
		||||
	};
 | 
			
		||||
	github?: {
 | 
			
		||||
		client_id: string;
 | 
			
		||||
		client_secret: string;
 | 
			
		||||
	};
 | 
			
		||||
	github_bot?: {
 | 
			
		||||
		hook_secret: string;
 | 
			
		||||
		username: string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,11 +82,6 @@ export interface ILocalUser extends IUserBase {
 | 
			
		|||
		userId: string;
 | 
			
		||||
		screenName: string;
 | 
			
		||||
	};
 | 
			
		||||
	github: {
 | 
			
		||||
		accessToken: string;
 | 
			
		||||
		id: string;
 | 
			
		||||
		login: string;
 | 
			
		||||
	};
 | 
			
		||||
	line: {
 | 
			
		||||
		userId: string;
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -285,9 +280,6 @@ export const pack = (
 | 
			
		|||
			delete _user.twitter.accessToken;
 | 
			
		||||
			delete _user.twitter.accessTokenSecret;
 | 
			
		||||
		}
 | 
			
		||||
		if (_user.github) {
 | 
			
		||||
			delete _user.github.accessToken;
 | 
			
		||||
		}
 | 
			
		||||
		delete _user.line;
 | 
			
		||||
 | 
			
		||||
		// Visible via only the official client
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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