From e8bde94e5bccf1303a1aec2f86544d59452bbb9d Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 2 Apr 2018 04:01:34 +0900
Subject: [PATCH] Refactor

---
 .../remote/activitypub/renderer/follow.ts     |  6 +++---
 src/common/remote/activitypub/renderer/key.ts | 10 +++++-----
 src/common/user/get-summary.ts                |  6 +++---
 src/models/user.ts                            | 20 ++++++++++++-------
 src/processor/http/follow.ts                  | 10 +++++-----
 src/server/activitypub/inbox.ts               |  7 ++++---
 src/server/api/bot/core.ts                    | 10 +++++-----
 src/server/api/endpoints/posts/create.ts      | 10 +++++-----
 src/server/api/private/signin.ts              |  8 ++++----
 9 files changed, 47 insertions(+), 40 deletions(-)

diff --git a/src/common/remote/activitypub/renderer/follow.ts b/src/common/remote/activitypub/renderer/follow.ts
index 05c0ecca06..86a3f8cede 100644
--- a/src/common/remote/activitypub/renderer/follow.ts
+++ b/src/common/remote/activitypub/renderer/follow.ts
@@ -1,8 +1,8 @@
 import config from '../../../../conf';
-import { IRemoteAccount } from '../../../../models/user';
+import { IRemoteUser } from '../../../../models/user';
 
-export default ({ username }, { account }) => ({
+export default ({ username }, followee: IRemoteUser) => ({
 	type: 'Follow',
 	actor: `${config.url}/@${username}`,
-	object: (account as IRemoteAccount).uri
+	object: followee.account.uri
 });
diff --git a/src/common/remote/activitypub/renderer/key.ts b/src/common/remote/activitypub/renderer/key.ts
index 692c71f88e..3cac86b769 100644
--- a/src/common/remote/activitypub/renderer/key.ts
+++ b/src/common/remote/activitypub/renderer/key.ts
@@ -1,10 +1,10 @@
 import config from '../../../../conf';
 import { extractPublic } from '../../../../crypto_key';
-import { ILocalAccount } from '../../../../models/user';
+import { ILocalUser } from '../../../../models/user';
 
-export default ({ username, account }) => ({
-	id: `${config.url}/@${username}/publickey`,
+export default (user: ILocalUser) => ({
+	id: `${config.url}/@${user.username}/publickey`,
 	type: 'Key',
-	owner: `${config.url}/@${username}`,
-	publicKeyPem: extractPublic((account as ILocalAccount).keypair)
+	owner: `${config.url}/@${user.username}`,
+	publicKeyPem: extractPublic(user.account.keypair)
 });
diff --git a/src/common/user/get-summary.ts b/src/common/user/get-summary.ts
index 47592c86ba..2c71d3eae9 100644
--- a/src/common/user/get-summary.ts
+++ b/src/common/user/get-summary.ts
@@ -1,4 +1,4 @@
-import { ILocalAccount, IUser } from '../../models/user';
+import { IUser, isLocalUser } from '../../models/user';
 import getAcct from './get-acct';
 
 /**
@@ -9,8 +9,8 @@ export default function(user: IUser): string {
 	let string = `${user.name} (@${getAcct(user)})\n` +
 		`${user.postsCount}投稿、${user.followingCount}フォロー、${user.followersCount}フォロワー\n`;
 
-	if (user.host === null) {
-		const account = user.account as ILocalAccount;
+	if (isLocalUser(user)) {
+		const account = user.account;
 		string += `場所: ${account.profile.location}、誕生日: ${account.profile.birthday}\n`;
 	}
 
diff --git a/src/models/user.ts b/src/models/user.ts
index d9ac72b88f..789b28b2f3 100644
--- a/src/models/user.ts
+++ b/src/models/user.ts
@@ -39,7 +39,7 @@ export function isValidBirthday(birthday: string): boolean {
 	return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday);
 }
 
-export type ILocalAccount = {
+type ILocalAccount = {
 	keypair: string;
 	email: string;
 	links: string[];
@@ -69,7 +69,7 @@ export type ILocalAccount = {
 	settings: any;
 };
 
-export type IRemoteAccount = {
+type IRemoteAccount = {
 	inbox: string;
 	uri: string;
 	publicKey: {
@@ -78,7 +78,7 @@ export type IRemoteAccount = {
 	};
 };
 
-export type IUser = {
+type IUserBase = {
 	_id: mongo.ObjectID;
 	createdAt: Date;
 	deletedAt: Date;
@@ -97,13 +97,19 @@ export type IUser = {
 	pinnedPostId: mongo.ObjectID;
 	isSuspended: boolean;
 	keywords: string[];
-	host: string;
 	hostLower: string;
-	account: ILocalAccount | IRemoteAccount;
 };
 
-export type ILocalUser = IUser & { account: ILocalAccount };
-export type IRemoteUser = IUser & { account: IRemoteAccount };
+export type IUser = ILocalUser | IRemoteUser;
+
+export interface ILocalUser extends IUserBase { host: null; account: ILocalAccount; }
+export interface IRemoteUser extends IUserBase { host: string; account: IRemoteAccount; }
+
+export const isLocalUser = (user: any): user is ILocalUser =>
+	user.host === null;
+
+export const isRemoteUser = (user: any): user is IRemoteUser =>
+	!isLocalUser(user);
 
 export function init(user): IUser {
 	user._id = new mongo.ObjectID(user._id);
diff --git a/src/processor/http/follow.ts b/src/processor/http/follow.ts
index a8f7ba78c8..9b8337f2e7 100644
--- a/src/processor/http/follow.ts
+++ b/src/processor/http/follow.ts
@@ -1,7 +1,7 @@
 import { request } from 'https';
 import { sign } from 'http-signature';
 import { URL } from 'url';
-import User, { ILocalAccount, IRemoteAccount, pack as packUser } from '../../models/user';
+import User, { isLocalUser, pack as packUser, ILocalUser } from '../../models/user';
 import Following from '../../models/following';
 import event from '../../common/event';
 import notify from '../../common/notify';
@@ -10,7 +10,7 @@ import render from '../../common/remote/activitypub/renderer/follow';
 import config from '../../conf';
 
 export default ({ data }, done) => Following.findOne({ _id: data.following }).then(({ followerId, followeeId }) => {
-	const promisedFollower = User.findOne({ _id: followerId });
+	const promisedFollower: Promise<ILocalUser> = User.findOne({ _id: followerId });
 	const promisedFollowee = User.findOne({ _id: followeeId });
 
 	return Promise.all([
@@ -38,7 +38,7 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th
 				.then(packed => event(follower._id, 'follow', packed));
 			let followeeEvent;
 
-			if (followee.host === null) {
+			if (isLocalUser(followee)) {
 				followeeEvent = packUser(follower, followee)
 					.then(packed => event(followee._id, 'followed', packed));
 			} else {
@@ -49,7 +49,7 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th
 						port,
 						pathname,
 						search
-					} = new URL((followee.account as IRemoteAccount).inbox);
+					} = new URL(followee.account.inbox);
 
 					const req = request({
 						protocol,
@@ -72,7 +72,7 @@ export default ({ data }, done) => Following.findOne({ _id: data.following }).th
 
 					sign(req, {
 						authorizationHeaderName: 'Signature',
-						key: (follower.account as ILocalAccount).keypair,
+						key: follower.account.keypair,
 						keyId: `acct:${follower.username}@${config.host}`
 					});
 
diff --git a/src/server/activitypub/inbox.ts b/src/server/activitypub/inbox.ts
index 6d092e66bf..cb679dbf0e 100644
--- a/src/server/activitypub/inbox.ts
+++ b/src/server/activitypub/inbox.ts
@@ -1,8 +1,9 @@
 import * as bodyParser from 'body-parser';
 import * as express from 'express';
 import { parseRequest, verifySignature } from 'http-signature';
-import User, { IRemoteAccount } from '../../models/user';
+import User, { IRemoteUser } from '../../models/user';
 import queue from '../../queue';
+import parseAcct from '../../common/user/parse-acct';
 
 const app = express();
 app.disable('x-powered-by');
@@ -36,13 +37,13 @@ app.post('/@:user/inbox', async (req, res) => {
 		};
 	}
 
-	const user = await User.findOne(query);
+	const user = await User.findOne(query) as IRemoteUser;
 
 	if (user === null) {
 		return res.sendStatus(401);
 	}
 
-	if (!verifySignature(parsed, (user.account as IRemoteAccount).publicKey.publicKeyPem)) {
+	if (!verifySignature(parsed, user.account.publicKey.publicKeyPem)) {
 		return res.sendStatus(401);
 	}
 
diff --git a/src/server/api/bot/core.ts b/src/server/api/bot/core.ts
index f84f1f5dca..d636cc26e7 100644
--- a/src/server/api/bot/core.ts
+++ b/src/server/api/bot/core.ts
@@ -1,7 +1,7 @@
 import * as EventEmitter from 'events';
 import * as bcrypt from 'bcryptjs';
 
-import User, { ILocalAccount, IUser, init as initUser } from '../../../models/user';
+import User, { IUser, init as initUser, ILocalUser } from '../../../models/user';
 
 import getPostSummary from '../../../common/get-post-summary';
 import getUserSummary from '../../../common/user/get-summary';
@@ -198,7 +198,7 @@ abstract class Context extends EventEmitter {
 }
 
 class SigninContext extends Context {
-	private temporaryUser: IUser = null;
+	private temporaryUser: ILocalUser = null;
 
 	public async greet(): Promise<string> {
 		return 'まずユーザー名を教えてください:';
@@ -207,14 +207,14 @@ class SigninContext extends Context {
 	public async q(query: string): Promise<string> {
 		if (this.temporaryUser == null) {
 			// Fetch user
-			const user: IUser = await User.findOne({
+			const user = await User.findOne({
 				usernameLower: query.toLowerCase(),
 				host: null
 			}, {
 				fields: {
 					data: false
 				}
-			});
+			}) as ILocalUser;
 
 			if (user === null) {
 				return `${query}というユーザーは存在しませんでした... もう一度教えてください:`;
@@ -225,7 +225,7 @@ class SigninContext extends Context {
 			}
 		} else {
 			// Compare password
-			const same = await bcrypt.compare(query, (this.temporaryUser.account as ILocalAccount).password);
+			const same = await bcrypt.compare(query, this.temporaryUser.account.password);
 
 			if (same) {
 				this.bot.signin(this.temporaryUser);
diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts
index 6e7d2329a7..4de9176947 100644
--- a/src/server/api/endpoints/posts/create.ts
+++ b/src/server/api/endpoints/posts/create.ts
@@ -5,9 +5,9 @@ import $ from 'cafy';
 import deepEqual = require('deep-equal');
 import html from '../../../../common/text/html';
 import parse from '../../../../common/text/parse';
-import { default as Post, IPost, isValidText, isValidCw } from '../../../../models/post';
-import { default as User, ILocalAccount, IUser } from '../../../../models/user';
-import { default as Channel, IChannel } from '../../../../models/channel';
+import Post, { IPost, isValidText, isValidCw } from '../../../../models/post';
+import User, { ILocalUser } from '../../../../models/user';
+import Channel, { IChannel } from '../../../../models/channel';
 import Following from '../../../../models/following';
 import Mute from '../../../../models/mute';
 import DriveFile from '../../../../models/drive-file';
@@ -29,7 +29,7 @@ import config from '../../../../conf';
  * @param {any} app
  * @return {Promise<any>}
  */
-module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
+module.exports = (params, user: ILocalUser, app) => new Promise(async (res, rej) => {
 	// Get 'text' parameter
 	const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$;
 	if (textErr) return rej('invalid text');
@@ -400,7 +400,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
 			});
 
 		// この投稿をWatchする
-		if ((user.account as ILocalAccount).settings.autoWatch !== false) {
+		if (user.account.settings.autoWatch !== false) {
 			watch(user._id, reply);
 		}
 
diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts
index 4b70644910..4ad5097e59 100644
--- a/src/server/api/private/signin.ts
+++ b/src/server/api/private/signin.ts
@@ -1,7 +1,7 @@
 import * as express from 'express';
 import * as bcrypt from 'bcryptjs';
 import * as speakeasy from 'speakeasy';
-import { default as User, ILocalAccount, IUser } from '../../../models/user';
+import User, { ILocalUser } from '../../../models/user';
 import Signin, { pack } from '../../../models/signin';
 import event from '../../../common/event';
 import signin from '../common/signin';
@@ -31,7 +31,7 @@ export default async (req: express.Request, res: express.Response) => {
 	}
 
 	// Fetch user
-	const user: IUser = await User.findOne({
+	const user = await User.findOne({
 		usernameLower: username.toLowerCase(),
 		host: null
 	}, {
@@ -39,7 +39,7 @@ export default async (req: express.Request, res: express.Response) => {
 			data: false,
 			'account.profile': false
 		}
-	});
+	}) as ILocalUser;
 
 	if (user === null) {
 		res.status(404).send({
@@ -48,7 +48,7 @@ export default async (req: express.Request, res: express.Response) => {
 		return;
 	}
 
-	const account = user.account as ILocalAccount;
+	const account = user.account;
 
 	// Compare password
 	const same = await bcrypt.compare(password, account.password);