From a02ee3a08bac55c9f0b29cb6bc0a15726b7cc3c8 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 8 Apr 2018 03:58:11 +0900
Subject: [PATCH] Some bug fixes

---
 src/client/app/common/define-widget.ts        |   4 +-
 src/client/app/common/mios.ts                 |  10 +-
 .../app/common/scripts/streaming/drive.ts     |   2 +-
 .../app/common/scripts/streaming/home.ts      |   4 +-
 .../scripts/streaming/messaging-index.ts      |   2 +-
 .../app/common/scripts/streaming/messaging.ts |   4 +-
 .../common/scripts/streaming/othello-game.ts  |   2 +-
 .../app/common/scripts/streaming/othello.ts   |   2 +-
 .../app/common/views/components/signin.vue    |   4 +-
 .../views/components/twitter-setting.vue      |  12 +-
 .../app/common/views/components/uploader.vue  |   2 +-
 src/client/app/desktop/api/update-avatar.ts   |   2 +-
 src/client/app/desktop/api/update-banner.ts   |   2 +-
 .../app/desktop/views/components/home.vue     |  25 +-
 .../desktop/views/components/note-detail.vue  |   4 +-
 .../desktop/views/components/notes.note.vue   |  27 +-
 .../desktop/views/components/post-detail.vue  |   4 +-
 .../desktop/views/components/posts.post.vue   |   6 +-
 .../desktop/views/components/settings.2fa.vue |   8 +-
 .../desktop/views/components/settings.api.vue |   2 +-
 .../views/components/settings.profile.vue     |   8 +-
 .../app/desktop/views/components/settings.vue |  12 +-
 .../app/desktop/views/components/timeline.vue |   2 +-
 .../desktop/views/components/ui.header.vue    |   8 +-
 .../views/components/widget-container.vue     |   4 +-
 .../app/desktop/views/components/window.vue   |   4 +-
 .../desktop/views/pages/user/user.header.vue  |   2 +-
 .../desktop/views/pages/user/user.home.vue    |   2 +-
 .../desktop/views/pages/user/user.profile.vue |  10 +-
 .../mobile/views/components/note-detail.vue   |   4 +-
 .../app/mobile/views/components/note.vue      |  32 +-
 .../mobile/views/components/post-detail.vue   | 462 ---------------
 .../app/mobile/views/components/post-form.vue |   2 +-
 .../app/mobile/views/components/post.vue      | 540 ------------------
 .../app/mobile/views/components/ui.header.vue |   4 +-
 src/client/app/mobile/views/pages/home.vue    |  20 +-
 .../mobile/views/pages/profile-setting.vue    |   4 +-
 src/client/app/mobile/views/pages/user.vue    |  10 +-
 .../app/mobile/views/pages/user/home.vue      |   2 +-
 src/client/app/mobile/views/pages/welcome.vue |   4 +-
 src/models/user.ts                            | 106 ++--
 src/queue/processors/http/process-inbox.ts    |   4 +-
 src/remote/activitypub/act/create/image.ts    |   2 +-
 src/remote/activitypub/act/create/index.ts    |   2 +-
 src/remote/activitypub/act/delete/index.ts    |   2 +-
 src/remote/activitypub/act/undo/index.ts      |   2 +-
 src/remote/activitypub/renderer/follow.ts     |   2 +-
 src/remote/activitypub/renderer/key.ts        |   2 +-
 src/remote/request.ts                         |   7 +-
 src/renderers/get-user-summary.ts             |   3 +-
 src/server/api/authenticate.ts                |   2 +-
 src/server/api/bot/core.ts                    |   2 +-
 src/server/api/bot/interfaces/line.ts         |  10 +-
 src/server/api/common/signin.ts               |   2 +-
 src/server/api/endpoints/following/create.ts  |   2 +-
 src/server/api/endpoints/following/delete.ts  |   2 +-
 src/server/api/endpoints/i.ts                 |   8 +-
 src/server/api/endpoints/i/2fa/done.ts        |   4 +-
 src/server/api/endpoints/i/2fa/register.ts    |   2 +-
 src/server/api/endpoints/i/2fa/unregister.ts  |   6 +-
 src/server/api/endpoints/i/change_password.ts |   4 +-
 .../api/endpoints/i/regenerate_token.ts       |   4 +-
 src/server/api/endpoints/i/update.ts          |  14 +-
 .../api/endpoints/i/update_client_setting.ts  |   4 +-
 src/server/api/endpoints/i/update_home.ts     |   6 +-
 .../api/endpoints/i/update_mobile_home.ts     |   6 +-
 src/server/api/endpoints/mute/create.ts       |   2 +-
 src/server/api/endpoints/mute/delete.ts       |   2 +-
 src/server/api/endpoints/notes/polls/vote.ts  |   2 +-
 .../api/endpoints/users/recommendation.ts     |   2 +-
 src/server/api/private/signin.ts              |  10 +-
 src/server/api/private/signup.ts              |  55 +-
 src/server/api/service/twitter.ts             |  10 +-
 src/server/api/stream/home.ts                 |   2 +-
 src/server/api/streaming.ts                   |   2 +-
 src/services/following/create.ts              |   4 +-
 src/services/following/delete.ts              |   2 +-
 src/services/note/create.ts                   |  10 +-
 src/services/note/reaction/create.ts          |   4 +-
 test/api.ts                                   |  16 +-
 tools/migration/nighthike/12.js               |  58 ++
 81 files changed, 337 insertions(+), 1318 deletions(-)
 delete mode 100644 src/client/app/mobile/views/components/post-detail.vue
 delete mode 100644 src/client/app/mobile/views/components/post.vue
 create mode 100644 tools/migration/nighthike/12.js

diff --git a/src/client/app/common/define-widget.ts b/src/client/app/common/define-widget.ts
index 9f8dcfc7ea..7b98c0903f 100644
--- a/src/client/app/common/define-widget.ts
+++ b/src/client/app/common/define-widget.ts
@@ -56,14 +56,14 @@ export default function<T extends object>(data: {
 						id: this.id,
 						data: newProps
 					}).then(() => {
-						(this as any).os.i.account.clientSettings.mobileHome.find(w => w.id == this.id).data = newProps;
+						(this as any).os.i.clientSettings.mobileHome.find(w => w.id == this.id).data = newProps;
 					});
 				} else {
 					(this as any).api('i/update_home', {
 						id: this.id,
 						data: newProps
 					}).then(() => {
-						(this as any).os.i.account.clientSettings.home.find(w => w.id == this.id).data = newProps;
+						(this as any).os.i.clientSettings.home.find(w => w.id == this.id).data = newProps;
 					});
 				}
 			}, {
diff --git a/src/client/app/common/mios.ts b/src/client/app/common/mios.ts
index 48e830810c..fd267bc3f8 100644
--- a/src/client/app/common/mios.ts
+++ b/src/client/app/common/mios.ts
@@ -270,7 +270,7 @@ export default class MiOS extends EventEmitter {
 				// Parse response
 				res.json().then(i => {
 					me = i;
-					me.account.token = token;
+					me.token = token;
 					done();
 				});
 			})
@@ -294,12 +294,12 @@ export default class MiOS extends EventEmitter {
 		const fetched = me => {
 			if (me) {
 				// デフォルトの設定をマージ
-				me.account.clientSettings = Object.assign({
+				me.clientSettings = Object.assign({
 					fetchOnScroll: true,
 					showMaps: true,
 					showPostFormOnTopOfTl: false,
 					gradientWindowHeader: false
-				}, me.account.clientSettings);
+				}, me.clientSettings);
 
 				// ローカルストレージにキャッシュ
 				localStorage.setItem('me', JSON.stringify(me));
@@ -329,7 +329,7 @@ export default class MiOS extends EventEmitter {
 			fetched(cachedMe);
 
 			// 後から新鮮なデータをフェッチ
-			fetchme(cachedMe.account.token, freshData => {
+			fetchme(cachedMe.token, freshData => {
 				merge(cachedMe, freshData);
 			});
 		} else {
@@ -437,7 +437,7 @@ export default class MiOS extends EventEmitter {
 		}
 
 		// Append a credential
-		if (this.isSignedIn) (data as any).i = this.i.account.token;
+		if (this.isSignedIn) (data as any).i = this.i.token;
 
 		// TODO
 		//const viaStream = localStorage.getItem('enableExperimental') == 'true';
diff --git a/src/client/app/common/scripts/streaming/drive.ts b/src/client/app/common/scripts/streaming/drive.ts
index f11573685e..7ff85b5946 100644
--- a/src/client/app/common/scripts/streaming/drive.ts
+++ b/src/client/app/common/scripts/streaming/drive.ts
@@ -8,7 +8,7 @@ import MiOS from '../../mios';
 export class DriveStream extends Stream {
 	constructor(os: MiOS, me) {
 		super(os, 'drive', {
-			i: me.account.token
+			i: me.token
 		});
 	}
 }
diff --git a/src/client/app/common/scripts/streaming/home.ts b/src/client/app/common/scripts/streaming/home.ts
index c198619400..e085801e15 100644
--- a/src/client/app/common/scripts/streaming/home.ts
+++ b/src/client/app/common/scripts/streaming/home.ts
@@ -10,13 +10,13 @@ import MiOS from '../../mios';
 export class HomeStream extends Stream {
 	constructor(os: MiOS, me) {
 		super(os, '', {
-			i: me.account.token
+			i: me.token
 		});
 
 		// 最終利用日時を更新するため定期的にaliveメッセージを送信
 		setInterval(() => {
 			this.send({ type: 'alive' });
-			me.account.lastUsedAt = new Date();
+			me.lastUsedAt = new Date();
 		}, 1000 * 60);
 
 		// 自分の情報が更新されたとき
diff --git a/src/client/app/common/scripts/streaming/messaging-index.ts b/src/client/app/common/scripts/streaming/messaging-index.ts
index 24f0ce0c9f..84e2174ec4 100644
--- a/src/client/app/common/scripts/streaming/messaging-index.ts
+++ b/src/client/app/common/scripts/streaming/messaging-index.ts
@@ -8,7 +8,7 @@ import MiOS from '../../mios';
 export class MessagingIndexStream extends Stream {
 	constructor(os: MiOS, me) {
 		super(os, 'messaging-index', {
-			i: me.account.token
+			i: me.token
 		});
 	}
 }
diff --git a/src/client/app/common/scripts/streaming/messaging.ts b/src/client/app/common/scripts/streaming/messaging.ts
index 4c593deb31..c1b5875cfb 100644
--- a/src/client/app/common/scripts/streaming/messaging.ts
+++ b/src/client/app/common/scripts/streaming/messaging.ts
@@ -7,13 +7,13 @@ import MiOS from '../../mios';
 export class MessagingStream extends Stream {
 	constructor(os: MiOS, me, otherparty) {
 		super(os, 'messaging', {
-			i: me.account.token,
+			i: me.token,
 			otherparty
 		});
 
 		(this as any).on('_connected_', () => {
 			this.send({
-				i: me.account.token
+				i: me.token
 			});
 		});
 	}
diff --git a/src/client/app/common/scripts/streaming/othello-game.ts b/src/client/app/common/scripts/streaming/othello-game.ts
index f34ef35147..b85af8f72b 100644
--- a/src/client/app/common/scripts/streaming/othello-game.ts
+++ b/src/client/app/common/scripts/streaming/othello-game.ts
@@ -4,7 +4,7 @@ import MiOS from '../../mios';
 export class OthelloGameStream extends Stream {
 	constructor(os: MiOS, me, game) {
 		super(os, 'othello-game', {
-			i: me ? me.account.token : null,
+			i: me ? me.token : null,
 			game: game.id
 		});
 	}
diff --git a/src/client/app/common/scripts/streaming/othello.ts b/src/client/app/common/scripts/streaming/othello.ts
index 8c6f4b9c3c..f5d47431cd 100644
--- a/src/client/app/common/scripts/streaming/othello.ts
+++ b/src/client/app/common/scripts/streaming/othello.ts
@@ -5,7 +5,7 @@ import MiOS from '../../mios';
 export class OthelloStream extends Stream {
 	constructor(os: MiOS, me) {
 		super(os, 'othello', {
-			i: me.account.token
+			i: me.token
 		});
 	}
 }
diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index 17154e6b31..da7472b8c7 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -6,7 +6,7 @@
 	<label class="password">
 		<input v-model="password" type="password" placeholder="%i18n:common.tags.mk-signin.password%" required/>%fa:lock%
 	</label>
-	<label class="token" v-if="user && user.account.twoFactorEnabled">
+	<label class="token" v-if="user && user.twoFactorEnabled">
 		<input v-model="token" type="number" placeholder="%i18n:common.tags.mk-signin.token%" required/>%fa:lock%
 	</label>
 	<button type="submit" :disabled="signing">{{ signing ? '%i18n:common.tags.mk-signin.signing-in%' : '%i18n:common.tags.mk-signin.signin%' }}</button>
@@ -43,7 +43,7 @@ export default Vue.extend({
 			(this as any).api('signin', {
 				username: this.username,
 				password: this.password,
-				token: this.user && this.user.account.twoFactorEnabled ? this.token : undefined
+				token: this.user && this.user.twoFactorEnabled ? this.token : undefined
 			}).then(() => {
 				location.reload();
 			}).catch(() => {
diff --git a/src/client/app/common/views/components/twitter-setting.vue b/src/client/app/common/views/components/twitter-setting.vue
index 082d2b435d..00669cd833 100644
--- a/src/client/app/common/views/components/twitter-setting.vue
+++ b/src/client/app/common/views/components/twitter-setting.vue
@@ -1,13 +1,13 @@
 <template>
 <div class="mk-twitter-setting">
 	<p>%i18n:common.tags.mk-twitter-setting.description%<a :href="`${docsUrl}/link-to-twitter`" target="_blank">%i18n:common.tags.mk-twitter-setting.detail%</a></p>
-	<p class="account" v-if="os.i.account.twitter" :title="`Twitter ID: ${os.i.account.twitter.userId}`">%i18n:common.tags.mk-twitter-setting.connected-to%: <a :href="`https://twitter.com/${os.i.account.twitter.screenName}`" target="_blank">@{{ os.i.account.twitter.screenName }}</a></p>
+	<p class="account" v-if="os.i.twitter" :title="`Twitter ID: ${os.i.twitter.userId}`">%i18n:common.tags.mk-twitter-setting.connected-to%: <a :href="`https://twitter.com/${os.i.twitter.screenName}`" target="_blank">@{{ os.i.twitter.screenName }}</a></p>
 	<p>
-		<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ os.i.account.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }}</a>
-		<span v-if="os.i.account.twitter"> or </span>
-		<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="os.i.account.twitter" @click.prevent="disconnect">%i18n:common.tags.mk-twitter-setting.disconnect%</a>
+		<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ os.i.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }}</a>
+		<span v-if="os.i.twitter"> or </span>
+		<a :href="`${apiUrl}/disconnect/twitter`" target="_blank" v-if="os.i.twitter" @click.prevent="disconnect">%i18n:common.tags.mk-twitter-setting.disconnect%</a>
 	</p>
-	<p class="id" v-if="os.i.account.twitter">Twitter ID: {{ os.i.account.twitter.userId }}</p>
+	<p class="id" v-if="os.i.twitter">Twitter ID: {{ os.i.twitter.userId }}</p>
 </div>
 </template>
 
@@ -25,7 +25,7 @@ export default Vue.extend({
 	},
 	mounted() {
 		this.$watch('os.i', () => {
-			if ((this as any).os.i.account.twitter) {
+			if ((this as any).os.i.twitter) {
 				if (this.form) this.form.close();
 			}
 		}, {
diff --git a/src/client/app/common/views/components/uploader.vue b/src/client/app/common/views/components/uploader.vue
index c74a1edb41..ccad50dc37 100644
--- a/src/client/app/common/views/components/uploader.vue
+++ b/src/client/app/common/views/components/uploader.vue
@@ -50,7 +50,7 @@ export default Vue.extend({
 			reader.readAsDataURL(file);
 
 			const data = new FormData();
-			data.append('i', (this as any).os.i.account.token);
+			data.append('i', (this as any).os.i.token);
 			data.append('file', file);
 
 			if (folder) data.append('folderId', folder);
diff --git a/src/client/app/desktop/api/update-avatar.ts b/src/client/app/desktop/api/update-avatar.ts
index 36a2ffe914..dc89adeb86 100644
--- a/src/client/app/desktop/api/update-avatar.ts
+++ b/src/client/app/desktop/api/update-avatar.ts
@@ -16,7 +16,7 @@ export default (os: OS) => (cb, file = null) => {
 
 		w.$once('cropped', blob => {
 			const data = new FormData();
-			data.append('i', os.i.account.token);
+			data.append('i', os.i.token);
 			data.append('file', blob, file.name + '.cropped.png');
 
 			os.api('drive/folders/find', {
diff --git a/src/client/app/desktop/api/update-banner.ts b/src/client/app/desktop/api/update-banner.ts
index e66dbf016b..5f10bf2856 100644
--- a/src/client/app/desktop/api/update-banner.ts
+++ b/src/client/app/desktop/api/update-banner.ts
@@ -16,7 +16,7 @@ export default (os: OS) => (cb, file = null) => {
 
 		w.$once('cropped', blob => {
 			const data = new FormData();
-			data.append('i', os.i.account.token);
+			data.append('i', os.i.token);
 			data.append('file', blob, file.name + '.cropped.png');
 
 			os.api('drive/folders/find', {
diff --git a/src/client/app/desktop/views/components/home.vue b/src/client/app/desktop/views/components/home.vue
index 7145ddce03..90e9d1b785 100644
--- a/src/client/app/desktop/views/components/home.vue
+++ b/src/client/app/desktop/views/components/home.vue
@@ -53,7 +53,7 @@
 			<div class="main">
 				<a @click="hint">カスタマイズのヒント</a>
 				<div>
-					<mk-post-form v-if="os.i.account.clientSettings.showPostFormOnTopOfTl"/>
+					<mk-post-form v-if="os.i.clientSettings.showPostFormOnTopOfTl"/>
 					<mk-timeline ref="tl" @loaded="onTlLoaded"/>
 				</div>
 			</div>
@@ -63,7 +63,7 @@
 				<component v-for="widget in widgets[place]" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" @chosen="warp"/>
 			</div>
 			<div class="main">
-				<mk-post-form v-if="os.i.account.clientSettings.showPostFormOnTopOfTl"/>
+				<mk-post-form v-if="os.i.clientSettings.showPostFormOnTopOfTl"/>
 				<mk-timeline ref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
 				<mk-mentions @loaded="onTlLoaded" v-if="mode == 'mentions'"/>
 			</div>
@@ -82,7 +82,10 @@ export default Vue.extend({
 		XDraggable
 	},
 	props: {
-		customize: Boolean,
+		customize: {
+			type: Boolean,
+			default: false
+		},
 		mode: {
 			type: String,
 			default: 'timeline'
@@ -104,16 +107,16 @@ export default Vue.extend({
 		home: {
 			get(): any[] {
 				//#region 互換性のため
-				(this as any).os.i.account.clientSettings.home.forEach(w => {
+				(this as any).os.i.clientSettings.home.forEach(w => {
 					if (w.name == 'rss-reader') w.name = 'rss';
 					if (w.name == 'user-recommendation') w.name = 'users';
 					if (w.name == 'recommended-polls') w.name = 'polls';
 				});
 				//#endregion
-				return (this as any).os.i.account.clientSettings.home;
+				return (this as any).os.i.clientSettings.home;
 			},
 			set(value) {
-				(this as any).os.i.account.clientSettings.home = value;
+				(this as any).os.i.clientSettings.home = value;
 			}
 		},
 		left(): any[] {
@@ -126,7 +129,7 @@ export default Vue.extend({
 	created() {
 		this.widgets.left = this.left;
 		this.widgets.right = this.right;
-		this.$watch('os.i.account.clientSettings', i => {
+		this.$watch('os.i.clientSettings', i => {
 			this.widgets.left = this.left;
 			this.widgets.right = this.right;
 		}, {
@@ -161,17 +164,17 @@ export default Vue.extend({
 		},
 		onHomeUpdated(data) {
 			if (data.home) {
-				(this as any).os.i.account.clientSettings.home = data.home;
+				(this as any).os.i.clientSettings.home = data.home;
 				this.widgets.left = data.home.filter(w => w.place == 'left');
 				this.widgets.right = data.home.filter(w => w.place == 'right');
 			} else {
-				const w = (this as any).os.i.account.clientSettings.home.find(w => w.id == data.id);
+				const w = (this as any).os.i.clientSettings.home.find(w => w.id == data.id);
 				if (w != null) {
 					w.data = data.data;
 					this.$refs[w.id][0].preventSave = true;
 					this.$refs[w.id][0].props = w.data;
-					this.widgets.left = (this as any).os.i.account.clientSettings.home.filter(w => w.place == 'left');
-					this.widgets.right = (this as any).os.i.account.clientSettings.home.filter(w => w.place == 'right');
+					this.widgets.left = (this as any).os.i.clientSettings.home.filter(w => w.place == 'left');
+					this.widgets.right = (this as any).os.i.clientSettings.home.filter(w => w.place == 'right');
 				}
 			}
 		},
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index 790f797ad6..df7c33dfa8 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -115,7 +115,7 @@ export default Vue.extend({
 		isRenote(): boolean {
 			return (this.note.renote &&
 				this.note.text == null &&
-				this.note.mediaIds == null &&
+				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
 		p(): any {
@@ -168,7 +168,7 @@ export default Vue.extend({
 
 		// Draw map
 		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
+			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
 			if (shouldShowMap) {
 				(this as any).os.getGoogleMaps().then(maps => {
 					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 924642c01a..4547ac58bb 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -5,25 +5,25 @@
 	</div>
 	<div class="renote" v-if="isRenote">
 		<p>
-			<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="note.userId">
+			<router-link class="avatar-anchor" :to="`/@${getAcct(note.user)}`" v-user-preview="note.userId">
 				<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
 			</router-link>
 			%fa:retweet%
 			<span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
-			<a class="name" :href="`/@${acct}`" v-user-preview="note.userId">{{ getUserName(note.user) }}</a>
+			<a class="name" :href="`/@${getAcct(note.user)}`" v-user-preview="note.userId">{{ getUserName(note.user) }}</a>
 			<span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
 		</p>
 		<mk-time :time="note.createdAt"/>
 	</div>
 	<article>
-		<router-link class="avatar-anchor" :to="`/@${acct}`">
+		<router-link class="avatar-anchor" :to="`/@${getAcct(p.user)}`">
 			<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="p.user.id"/>
 		</router-link>
 		<div class="main">
 			<header>
-				<router-link class="name" :to="`/@${acct}`" v-user-preview="p.user.id">{{ acct }}</router-link>
-				<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
-				<span class="username">@{{ acct }}</span>
+				<router-link class="name" :to="`/@${getAcct(p.user)}`" v-user-preview="p.user.id">{{ getUserName(p) }}</router-link>
+				<span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
+				<span class="username">@{{ getAcct(p.user) }}</span>
 				<div class="info">
 					<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
 					<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
@@ -117,21 +117,18 @@ export default Vue.extend({
 		return {
 			isDetailOpened: false,
 			connection: null,
-			connectionId: null
+			connectionId: null,
+			getAcct,
+			getUserName
 		};
 	},
 
 	computed: {
-		acct(): string {
-			return getAcct(this.p.user);
-		},
-		name(): string {
-			return getUserName(this.p.user);
-		},
+
 		isRenote(): boolean {
 			return (this.note.renote &&
 				this.note.text == null &&
-				this.note.mediaIds == null &&
+				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
 		p(): any {
@@ -178,7 +175,7 @@ export default Vue.extend({
 
 		// Draw map
 		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
+			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
 			if (shouldShowMap) {
 				(this as any).os.getGoogleMaps().then(maps => {
 					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
diff --git a/src/client/app/desktop/views/components/post-detail.vue b/src/client/app/desktop/views/components/post-detail.vue
index 790f797ad6..df7c33dfa8 100644
--- a/src/client/app/desktop/views/components/post-detail.vue
+++ b/src/client/app/desktop/views/components/post-detail.vue
@@ -115,7 +115,7 @@ export default Vue.extend({
 		isRenote(): boolean {
 			return (this.note.renote &&
 				this.note.text == null &&
-				this.note.mediaIds == null &&
+				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
 		p(): any {
@@ -168,7 +168,7 @@ export default Vue.extend({
 
 		// Draw map
 		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
+			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
 			if (shouldShowMap) {
 				(this as any).os.getGoogleMaps().then(maps => {
 					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
diff --git a/src/client/app/desktop/views/components/posts.post.vue b/src/client/app/desktop/views/components/posts.post.vue
index 924642c01a..d7c21dfa07 100644
--- a/src/client/app/desktop/views/components/posts.post.vue
+++ b/src/client/app/desktop/views/components/posts.post.vue
@@ -22,7 +22,7 @@
 		<div class="main">
 			<header>
 				<router-link class="name" :to="`/@${acct}`" v-user-preview="p.user.id">{{ acct }}</router-link>
-				<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
+				<span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
 				<span class="username">@{{ acct }}</span>
 				<div class="info">
 					<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
@@ -131,7 +131,7 @@ export default Vue.extend({
 		isRenote(): boolean {
 			return (this.note.renote &&
 				this.note.text == null &&
-				this.note.mediaIds == null &&
+				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
 		p(): any {
@@ -178,7 +178,7 @@ export default Vue.extend({
 
 		// Draw map
 		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
+			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
 			if (shouldShowMap) {
 				(this as any).os.getGoogleMaps().then(maps => {
 					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
diff --git a/src/client/app/desktop/views/components/settings.2fa.vue b/src/client/app/desktop/views/components/settings.2fa.vue
index b8dd1dfd9b..fb2dc30f2a 100644
--- a/src/client/app/desktop/views/components/settings.2fa.vue
+++ b/src/client/app/desktop/views/components/settings.2fa.vue
@@ -2,8 +2,8 @@
 <div class="2fa">
 	<p>%i18n:desktop.tags.mk-2fa-setting.intro%<a href="%i18n:desktop.tags.mk-2fa-setting.url%" target="_blank">%i18n:desktop.tags.mk-2fa-setting.detail%</a></p>
 	<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-2fa-setting.caution%</p></div>
-	<p v-if="!data && !os.i.account.twoFactorEnabled"><button @click="register" class="ui primary">%i18n:desktop.tags.mk-2fa-setting.register%</button></p>
-	<template v-if="os.i.account.twoFactorEnabled">
+	<p v-if="!data && !os.i.twoFactorEnabled"><button @click="register" class="ui primary">%i18n:desktop.tags.mk-2fa-setting.register%</button></p>
+	<template v-if="os.i.twoFactorEnabled">
 		<p>%i18n:desktop.tags.mk-2fa-setting.already-registered%</p>
 		<button @click="unregister" class="ui">%i18n:desktop.tags.mk-2fa-setting.unregister%</button>
 	</template>
@@ -54,7 +54,7 @@ export default Vue.extend({
 					password: password
 				}).then(() => {
 					(this as any).apis.notify('%i18n:desktop.tags.mk-2fa-setting.unregistered%');
-					(this as any).os.i.account.twoFactorEnabled = false;
+					(this as any).os.i.twoFactorEnabled = false;
 				});
 			});
 		},
@@ -64,7 +64,7 @@ export default Vue.extend({
 				token: this.token
 			}).then(() => {
 				(this as any).apis.notify('%i18n:desktop.tags.mk-2fa-setting.success%');
-				(this as any).os.i.account.twoFactorEnabled = true;
+				(this as any).os.i.twoFactorEnabled = true;
 			}).catch(() => {
 				(this as any).apis.notify('%i18n:desktop.tags.mk-2fa-setting.failed%');
 			});
diff --git a/src/client/app/desktop/views/components/settings.api.vue b/src/client/app/desktop/views/components/settings.api.vue
index 0d5921ab7f..5831f82075 100644
--- a/src/client/app/desktop/views/components/settings.api.vue
+++ b/src/client/app/desktop/views/components/settings.api.vue
@@ -1,6 +1,6 @@
 <template>
 <div class="root api">
-	<p>Token: <code>{{ os.i.account.token }}</code></p>
+	<p>Token: <code>{{ os.i.token }}</code></p>
 	<p>%i18n:desktop.tags.mk-api-info.intro%</p>
 	<div class="ui info warn"><p>%fa:exclamation-triangle%%i18n:desktop.tags.mk-api-info.caution%</p></div>
 	<p>%i18n:desktop.tags.mk-api-info.regeneration-of-token%</p>
diff --git a/src/client/app/desktop/views/components/settings.profile.vue b/src/client/app/desktop/views/components/settings.profile.vue
index 28be48e0a8..324a939f95 100644
--- a/src/client/app/desktop/views/components/settings.profile.vue
+++ b/src/client/app/desktop/views/components/settings.profile.vue
@@ -24,7 +24,7 @@
 	<button class="ui primary" @click="save">%i18n:desktop.tags.mk-profile-setting.save%</button>
 	<section>
 		<h2>その他</h2>
-		<mk-switch v-model="os.i.account.isBot" @change="onChangeIsBot" text="このアカウントはbotです"/>
+		<mk-switch v-model="os.i.isBot" @change="onChangeIsBot" text="このアカウントはbotです"/>
 	</section>
 </div>
 </template>
@@ -43,9 +43,9 @@ export default Vue.extend({
 	},
 	created() {
 		this.name = (this as any).os.i.name || '';
-		this.location = (this as any).os.i.account.profile.location;
+		this.location = (this as any).os.i.profile.location;
 		this.description = (this as any).os.i.description;
-		this.birthday = (this as any).os.i.account.profile.birthday;
+		this.birthday = (this as any).os.i.profile.birthday;
 	},
 	methods: {
 		updateAvatar() {
@@ -63,7 +63,7 @@ export default Vue.extend({
 		},
 		onChangeIsBot() {
 			(this as any).api('i/update', {
-				isBot: (this as any).os.i.account.isBot
+				isBot: (this as any).os.i.isBot
 			});
 		}
 	}
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index fd82c171c1..4184ae82c7 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -20,7 +20,7 @@
 
 		<section class="web" v-show="page == 'web'">
 			<h1>動作</h1>
-			<mk-switch v-model="os.i.account.clientSettings.fetchOnScroll" @change="onChangeFetchOnScroll" text="スクロールで自動読み込み">
+			<mk-switch v-model="os.i.clientSettings.fetchOnScroll" @change="onChangeFetchOnScroll" text="スクロールで自動読み込み">
 				<span>ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。</span>
 			</mk-switch>
 			<mk-switch v-model="autoPopout" text="ウィンドウの自動ポップアウト">
@@ -33,11 +33,11 @@
 			<div class="div">
 				<button class="ui button" @click="customizeHome">ホームをカスタマイズ</button>
 			</div>
-			<mk-switch v-model="os.i.account.clientSettings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="タイムライン上部に投稿フォームを表示する"/>
-			<mk-switch v-model="os.i.account.clientSettings.showMaps" @change="onChangeShowMaps" text="マップの自動展開">
+			<mk-switch v-model="os.i.clientSettings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="タイムライン上部に投稿フォームを表示する"/>
+			<mk-switch v-model="os.i.clientSettings.showMaps" @change="onChangeShowMaps" text="マップの自動展開">
 				<span>位置情報が添付された投稿のマップを自動的に展開します。</span>
 			</mk-switch>
-			<mk-switch v-model="os.i.account.clientSettings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="ウィンドウのタイトルバーにグラデーションを使用"/>
+			<mk-switch v-model="os.i.clientSettings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="ウィンドウのタイトルバーにグラデーションを使用"/>
 		</section>
 
 		<section class="web" v-show="page == 'web'">
@@ -57,7 +57,7 @@
 
 		<section class="web" v-show="page == 'web'">
 			<h1>モバイル</h1>
-			<mk-switch v-model="os.i.account.clientSettings.disableViaMobile" @change="onChangeDisableViaMobile" text="「モバイルからの投稿」フラグを付けない"/>
+			<mk-switch v-model="os.i.clientSettings.disableViaMobile" @change="onChangeDisableViaMobile" text="「モバイルからの投稿」フラグを付けない"/>
 		</section>
 
 		<section class="web" v-show="page == 'web'">
@@ -86,7 +86,7 @@
 
 		<section class="notification" v-show="page == 'notification'">
 			<h1>通知</h1>
-			<mk-switch v-model="os.i.account.settings.autoWatch" @change="onChangeAutoWatch" text="投稿の自動ウォッチ">
+			<mk-switch v-model="os.i.settings.autoWatch" @change="onChangeAutoWatch" text="投稿の自動ウォッチ">
 				<span>リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。</span>
 			</mk-switch>
 		</section>
diff --git a/src/client/app/desktop/views/components/timeline.vue b/src/client/app/desktop/views/components/timeline.vue
index 6d049eee9c..ea8f0053bc 100644
--- a/src/client/app/desktop/views/components/timeline.vue
+++ b/src/client/app/desktop/views/components/timeline.vue
@@ -107,7 +107,7 @@ export default Vue.extend({
 			this.fetch();
 		},
 		onScroll() {
-			if ((this as any).os.i.account.clientSettings.fetchOnScroll !== false) {
+			if ((this as any).os.i.clientSettings.fetchOnScroll !== false) {
 				const current = window.scrollY + window.innerHeight;
 				if (current > document.body.offsetHeight - 8) this.more();
 			}
diff --git a/src/client/app/desktop/views/components/ui.header.vue b/src/client/app/desktop/views/components/ui.header.vue
index 7d93847fab..527d10843a 100644
--- a/src/client/app/desktop/views/components/ui.header.vue
+++ b/src/client/app/desktop/views/components/ui.header.vue
@@ -37,8 +37,8 @@ import getUserName from '../../../../../renderers/get-user-name';
 
 export default Vue.extend({
 	computed: {
-		name() {
-			return getUserName(this.os.i);
+		name(): string {
+			return getUserName((this as any).os.i);
 		}
 	},
 	components: {
@@ -51,9 +51,9 @@ export default Vue.extend({
 	},
 	mounted() {
 		if ((this as any).os.isSignedIn) {
-			const ago = (new Date().getTime() - new Date((this as any).os.i.account.lastUsedAt).getTime()) / 1000
+			const ago = (new Date().getTime() - new Date((this as any).os.i.lastUsedAt).getTime()) / 1000
 			const isHisasiburi = ago >= 3600;
-			(this as any).os.i.account.lastUsedAt = new Date();
+			(this as any).os.i.lastUsedAt = new Date();
 			if (isHisasiburi) {
 				(this.$refs.welcomeback as any).style.display = 'block';
 				(this.$refs.main as any).style.overflow = 'hidden';
diff --git a/src/client/app/desktop/views/components/widget-container.vue b/src/client/app/desktop/views/components/widget-container.vue
index 68c5bcb8dc..188a67313e 100644
--- a/src/client/app/desktop/views/components/widget-container.vue
+++ b/src/client/app/desktop/views/components/widget-container.vue
@@ -24,8 +24,8 @@ export default Vue.extend({
 	computed: {
 		withGradient(): boolean {
 			return (this as any).os.isSignedIn
-				? (this as any).os.i.account.clientSettings.gradientWindowHeader != null
-					? (this as any).os.i.account.clientSettings.gradientWindowHeader
+				? (this as any).os.i.clientSettings.gradientWindowHeader != null
+					? (this as any).os.i.clientSettings.gradientWindowHeader
 					: false
 				: false;
 		}
diff --git a/src/client/app/desktop/views/components/window.vue b/src/client/app/desktop/views/components/window.vue
index 48dc46febd..e2cab21799 100644
--- a/src/client/app/desktop/views/components/window.vue
+++ b/src/client/app/desktop/views/components/window.vue
@@ -92,8 +92,8 @@ export default Vue.extend({
 		},
 		withGradient(): boolean {
 			return (this as any).os.isSignedIn
-				? (this as any).os.i.account.clientSettings.gradientWindowHeader != null
-					? (this as any).os.i.account.clientSettings.gradientWindowHeader
+				? (this as any).os.i.clientSettings.gradientWindowHeader != null
+					? (this as any).os.i.clientSettings.gradientWindowHeader
 					: false
 				: false;
 		}
diff --git a/src/client/app/desktop/views/pages/user/user.header.vue b/src/client/app/desktop/views/pages/user/user.header.vue
index 5c6746d5dc..ceeb784315 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -9,7 +9,7 @@
 		<div class="title">
 			<p class="name">{{ name }}</p>
 			<p class="username">@{{ acct }}</p>
-			<p class="location" v-if="user.host === null && user.account.profile.location">%fa:map-marker%{{ user.account.profile.location }}</p>
+			<p class="location" v-if="user.host === null && user.profile.location">%fa:map-marker%{{ user.profile.location }}</p>
 		</div>
 		<footer>
 			<router-link :to="`/@${acct}`" :data-active="$parent.page == 'home'">%fa:home%概要</router-link>
diff --git a/src/client/app/desktop/views/pages/user/user.home.vue b/src/client/app/desktop/views/pages/user/user.home.vue
index ed3b1c7104..c254a320ce 100644
--- a/src/client/app/desktop/views/pages/user/user.home.vue
+++ b/src/client/app/desktop/views/pages/user/user.home.vue
@@ -5,7 +5,7 @@
 			<x-profile :user="user"/>
 			<x-photos :user="user"/>
 			<x-followers-you-know v-if="os.isSignedIn && os.i.id != user.id" :user="user"/>
-			<p v-if="user.host === null">%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time :time="user.account.lastUsedAt"/></b></p>
+			<p v-if="user.host === null">%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time :time="user.lastUsedAt"/></b></p>
 		</div>
 	</div>
 	<main>
diff --git a/src/client/app/desktop/views/pages/user/user.profile.vue b/src/client/app/desktop/views/pages/user/user.profile.vue
index 2a82ba7861..44f20c2bc0 100644
--- a/src/client/app/desktop/views/pages/user/user.profile.vue
+++ b/src/client/app/desktop/views/pages/user/user.profile.vue
@@ -7,11 +7,11 @@
 		<p v-if="!user.isMuted"><a @click="mute">%i18n:desktop.tags.mk-user.mute%</a></p>
 	</div>
 	<div class="description" v-if="user.description">{{ user.description }}</div>
-	<div class="birthday" v-if="user.host === null && user.account.profile.birthday">
-		<p>%fa:birthday-cake%{{ user.account.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)</p>
+	<div class="birthday" v-if="user.host === null && user.profile.birthday">
+		<p>%fa:birthday-cake%{{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)</p>
 	</div>
-	<div class="twitter" v-if="user.host === null && user.account.twitter">
-		<p>%fa:B twitter%<a :href="`https://twitter.com/${user.account.twitter.screenName}`" target="_blank">@{{ user.account.twitter.screenName }}</a></p>
+	<div class="twitter" v-if="user.host === null && user.twitter">
+		<p>%fa:B twitter%<a :href="`https://twitter.com/${user.twitter.screenName}`" target="_blank">@{{ user.twitter.screenName }}</a></p>
 	</div>
 	<div class="status">
 		<p class="notes-count">%fa:angle-right%<a>{{ user.notesCount }}</a><b>投稿</b></p>
@@ -31,7 +31,7 @@ export default Vue.extend({
 	props: ['user'],
 	computed: {
 		age(): number {
-			return age(this.user.account.profile.birthday);
+			return age(this.user.profile.birthday);
 		}
 	},
 	methods: {
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index e1682e58ed..483f5aaf3b 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -127,7 +127,7 @@ export default Vue.extend({
 		isRenote(): boolean {
 			return (this.note.renote &&
 				this.note.text == null &&
-				this.note.mediaIds == null &&
+				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
 		p(): any {
@@ -165,7 +165,7 @@ export default Vue.extend({
 
 		// Draw map
 		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
+			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
 			if (shouldShowMap) {
 				(this as any).os.getGoogleMaps().then(maps => {
 					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 4b33c6f071..295fe4d6a8 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -5,25 +5,25 @@
 	</div>
 	<div class="renote" v-if="isRenote">
 		<p>
-			<router-link class="avatar-anchor" :to="`/@${acct}`">
+			<router-link class="avatar-anchor" :to="`/@${getAcct(note.user)}`">
 				<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
 			</router-link>
 			%fa:retweet%
 			<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
-			<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
+			<router-link class="name" :to="`/@${getAcct(note.user)}`">{{ getUserName(note.user) }}</router-link>
 			<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
 		</p>
 		<mk-time :time="note.createdAt"/>
 	</div>
 	<article>
-		<router-link class="avatar-anchor" :to="`/@${pAcct}`">
+		<router-link class="avatar-anchor" :to="`/@${getAcct(p.user)}`">
 			<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
 		</router-link>
 		<div class="main">
 			<header>
-				<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
-				<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
-				<span class="username">@{{ pAcct }}</span>
+				<router-link class="name" :to="`/@${getAcct(p.user)}`">{{ getUserName(p.user) }}</router-link>
+				<span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
+				<span class="username">@{{ getAcct(p.user) }}</span>
 				<div class="info">
 					<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
 					<router-link class="created-at" :to="url">
@@ -95,27 +95,17 @@ export default Vue.extend({
 	data() {
 		return {
 			connection: null,
-			connectionId: null
+			connectionId: null,
+			getAcct,
+			getUserName
 		};
 	},
 
 	computed: {
-		acct(): string {
-			return getAcct(this.note.user);
-		},
-		name(): string {
-			return getUserName(this.note.user);
-		},
-		pAcct(): string {
-			return getAcct(this.p.user);
-		},
-		pName(): string {
-			return getUserName(this.p.user);
-		},
 		isRenote(): boolean {
 			return (this.note.renote &&
 				this.note.text == null &&
-				this.note.mediaIds == null &&
+				this.note.mediaIds.length == 0 &&
 				this.note.poll == null);
 		},
 		p(): any {
@@ -159,7 +149,7 @@ export default Vue.extend({
 
 		// Draw map
 		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
+			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
 			if (shouldShowMap) {
 				(this as any).os.getGoogleMaps().then(maps => {
 					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
diff --git a/src/client/app/mobile/views/components/post-detail.vue b/src/client/app/mobile/views/components/post-detail.vue
deleted file mode 100644
index e1682e58ed..0000000000
--- a/src/client/app/mobile/views/components/post-detail.vue
+++ /dev/null
@@ -1,462 +0,0 @@
-<template>
-<div class="mk-note-detail">
-	<button
-		class="more"
-		v-if="p.reply && p.reply.replyId && context == null"
-		@click="fetchContext"
-		:disabled="fetchingContext"
-	>
-		<template v-if="!contextFetching">%fa:ellipsis-v%</template>
-		<template v-if="contextFetching">%fa:spinner .pulse%</template>
-	</button>
-	<div class="context">
-		<x-sub v-for="note in context" :key="note.id" :note="note"/>
-	</div>
-	<div class="reply-to" v-if="p.reply">
-		<x-sub :note="p.reply"/>
-	</div>
-	<div class="renote" v-if="isRenote">
-		<p>
-			<router-link class="avatar-anchor" :to="`/@${acct}`">
-				<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
-			</router-link>
-			%fa:retweet%
-			<router-link class="name" :to="`/@${acct}`">
-				{{ name }}
-			</router-link>
-			がRenote
-		</p>
-	</div>
-	<article>
-		<header>
-			<router-link class="avatar-anchor" :to="`/@${pAcct}`">
-				<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
-			</router-link>
-			<div>
-				<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
-				<span class="username">@{{ pAcct }}</span>
-			</div>
-		</header>
-		<div class="body">
-			<mk-note-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
-			<div class="tags" v-if="p.tags && p.tags.length > 0">
-				<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
-			</div>
-			<div class="media" v-if="p.media.length > 0">
-				<mk-media-list :media-list="p.media"/>
-			</div>
-			<mk-poll v-if="p.poll" :note="p"/>
-			<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
-			<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
-			<div class="map" v-if="p.geo" ref="map"></div>
-			<div class="renote" v-if="p.renote">
-				<mk-note-preview :note="p.renote"/>
-			</div>
-		</div>
-		<router-link class="time" :to="`/@${pAcct}/${p.id}`">
-			<mk-time :time="p.createdAt" mode="detail"/>
-		</router-link>
-		<footer>
-			<mk-reactions-viewer :note="p"/>
-			<button @click="reply" title="%i18n:mobile.tags.mk-note-detail.reply%">
-				%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
-			</button>
-			<button @click="renote" title="Renote">
-				%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
-			</button>
-			<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-note-detail.reaction%">
-				%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
-			</button>
-			<button @click="menu" ref="menuButton">
-				%fa:ellipsis-h%
-			</button>
-		</footer>
-	</article>
-	<div class="replies" v-if="!compact">
-		<x-sub v-for="note in replies" :key="note.id" :note="note"/>
-	</div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../acct/render';
-import getUserName from '../../../../../renderers/get-user-name';
-import parse from '../../../../../text/parse';
-
-import MkNoteMenu from '../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './note-detail.sub.vue';
-
-export default Vue.extend({
-	components: {
-		XSub
-	},
-
-	props: {
-		note: {
-			type: Object,
-			required: true
-		},
-		compact: {
-			default: false
-		}
-	},
-
-	data() {
-		return {
-			context: [],
-			contextFetching: false,
-			replies: []
-		};
-	},
-
-	computed: {
-		acct(): string {
-			return getAcct(this.note.user);
-		},
-		name(): string {
-			return getUserName(this.note.user);
-		},
-		pAcct(): string {
-			return getAcct(this.p.user);
-		},
-		pName(): string {
-			return getUserName(this.p.user);
-		},
-		isRenote(): boolean {
-			return (this.note.renote &&
-				this.note.text == null &&
-				this.note.mediaIds == null &&
-				this.note.poll == null);
-		},
-		p(): any {
-			return this.isRenote ? this.note.renote : this.note;
-		},
-		reactionsCount(): number {
-			return this.p.reactionCounts
-				? Object.keys(this.p.reactionCounts)
-					.map(key => this.p.reactionCounts[key])
-					.reduce((a, b) => a + b)
-				: 0;
-		},
-		urls(): string[] {
-			if (this.p.text) {
-				const ast = parse(this.p.text);
-				return ast
-					.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
-					.map(t => t.url);
-			} else {
-				return null;
-			}
-		}
-	},
-
-	mounted() {
-		// Get replies
-		if (!this.compact) {
-			(this as any).api('notes/replies', {
-				noteId: this.p.id,
-				limit: 8
-			}).then(replies => {
-				this.replies = replies;
-			});
-		}
-
-		// Draw map
-		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
-			if (shouldShowMap) {
-				(this as any).os.getGoogleMaps().then(maps => {
-					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
-					const map = new maps.Map(this.$refs.map, {
-						center: uluru,
-						zoom: 15
-					});
-					new maps.Marker({
-						position: uluru,
-						map: map
-					});
-				});
-			}
-		}
-	},
-
-	methods: {
-		fetchContext() {
-			this.contextFetching = true;
-
-			// Fetch context
-			(this as any).api('notes/context', {
-				noteId: this.p.replyId
-			}).then(context => {
-				this.contextFetching = false;
-				this.context = context.reverse();
-			});
-		},
-		reply() {
-			(this as any).apis.post({
-				reply: this.p
-			});
-		},
-		renote() {
-			(this as any).apis.post({
-				renote: this.p
-			});
-		},
-		react() {
-			(this as any).os.new(MkReactionPicker, {
-				source: this.$refs.reactButton,
-				note: this.p,
-				compact: true
-			});
-		},
-		menu() {
-			(this as any).os.new(MkNoteMenu, {
-				source: this.$refs.menuButton,
-				note: this.p,
-				compact: true
-			});
-		}
-	}
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-note-detail
-	overflow hidden
-	margin 0 auto
-	padding 0
-	width 100%
-	text-align left
-	background #fff
-	border-radius 8px
-	box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
-	> .fetching
-		padding 64px 0
-
-	> .more
-		display block
-		margin 0
-		padding 10px 0
-		width 100%
-		font-size 1em
-		text-align center
-		color #999
-		cursor pointer
-		background #fafafa
-		outline none
-		border none
-		border-bottom solid 1px #eef0f2
-		border-radius 6px 6px 0 0
-		box-shadow none
-
-		&:hover
-			background #f6f6f6
-
-		&:active
-			background #f0f0f0
-
-		&:disabled
-			color #ccc
-
-	> .context
-		> *
-			border-bottom 1px solid #eef0f2
-
-	> .renote
-		color #9dbb00
-		background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
-
-		> p
-			margin 0
-			padding 16px 32px
-
-			.avatar-anchor
-				display inline-block
-
-				.avatar
-					vertical-align bottom
-					min-width 28px
-					min-height 28px
-					max-width 28px
-					max-height 28px
-					margin 0 8px 0 0
-					border-radius 6px
-
-			[data-fa]
-				margin-right 4px
-
-			.name
-				font-weight bold
-
-		& + article
-			padding-top 8px
-
-	> .reply-to
-		border-bottom 1px solid #eef0f2
-
-	> article
-		padding 14px 16px 9px 16px
-
-		@media (min-width 500px)
-			padding 28px 32px 18px 32px
-
-		&:after
-			content ""
-			display block
-			clear both
-
-		&:hover
-			> .main > footer > button
-				color #888
-
-		> header
-			display flex
-			line-height 1.1
-
-			> .avatar-anchor
-				display block
-				padding 0 .5em 0 0
-
-				> .avatar
-					display block
-					width 54px
-					height 54px
-					margin 0
-					border-radius 8px
-					vertical-align bottom
-
-					@media (min-width 500px)
-						width 60px
-						height 60px
-
-			> div
-
-				> .name
-					display inline-block
-					margin .4em 0
-					color #777
-					font-size 16px
-					font-weight bold
-					text-align left
-					text-decoration none
-
-					&:hover
-						text-decoration underline
-
-				> .username
-					display block
-					text-align left
-					margin 0
-					color #ccc
-
-		> .body
-			padding 8px 0
-
-			> .renote
-				margin 8px 0
-
-				> .mk-note-preview
-					padding 16px
-					border dashed 1px #c0dac6
-					border-radius 8px
-
-			> .location
-				margin 4px 0
-				font-size 12px
-				color #ccc
-
-			> .map
-				width 100%
-				height 200px
-
-				&:empty
-					display none
-
-			> .mk-url-preview
-				margin-top 8px
-
-			> .media
-				> img
-					display block
-					max-width 100%
-
-			> .tags
-				margin 4px 0 0 0
-
-				> *
-					display inline-block
-					margin 0 8px 0 0
-					padding 2px 8px 2px 16px
-					font-size 90%
-					color #8d969e
-					background #edf0f3
-					border-radius 4px
-
-					&:before
-						content ""
-						display block
-						position absolute
-						top 0
-						bottom 0
-						left 4px
-						width 8px
-						height 8px
-						margin auto 0
-						background #fff
-						border-radius 100%
-
-		> .time
-			font-size 16px
-			color #c0c0c0
-
-		> footer
-			font-size 1.2em
-
-			> button
-				margin 0
-				padding 8px
-				background transparent
-				border none
-				box-shadow none
-				font-size 1em
-				color #ddd
-				cursor pointer
-
-				&:not(:last-child)
-					margin-right 28px
-
-				&:hover
-					color #666
-
-				> .count
-					display inline
-					margin 0 0 0 8px
-					color #999
-
-				&.reacted
-					color $theme-color
-
-	> .replies
-		> *
-			border-top 1px solid #eef0f2
-
-</style>
-
-<style lang="stylus" module>
-.text
-	display block
-	margin 0
-	padding 0
-	overflow-wrap break-word
-	font-size 16px
-	color #717171
-
-	@media (min-width 500px)
-		font-size 24px
-
-</style>
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index 6fcbbb47e8..70be6db7b2 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -111,7 +111,7 @@ export default Vue.extend({
 		},
 		post() {
 			this.posting = true;
-			const viaMobile = (this as any).os.i.account.clientSettings.disableViaMobile !== true;
+			const viaMobile = (this as any).os.i.clientSettings.disableViaMobile !== true;
 			(this as any).api('notes/create', {
 				text: this.text == '' ? undefined : this.text,
 				mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
diff --git a/src/client/app/mobile/views/components/post.vue b/src/client/app/mobile/views/components/post.vue
deleted file mode 100644
index 4b33c6f071..0000000000
--- a/src/client/app/mobile/views/components/post.vue
+++ /dev/null
@@ -1,540 +0,0 @@
-<template>
-<div class="note" :class="{ renote: isRenote }">
-	<div class="reply-to" v-if="p.reply">
-		<x-sub :note="p.reply"/>
-	</div>
-	<div class="renote" v-if="isRenote">
-		<p>
-			<router-link class="avatar-anchor" :to="`/@${acct}`">
-				<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
-			</router-link>
-			%fa:retweet%
-			<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
-			<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
-			<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
-		</p>
-		<mk-time :time="note.createdAt"/>
-	</div>
-	<article>
-		<router-link class="avatar-anchor" :to="`/@${pAcct}`">
-			<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
-		</router-link>
-		<div class="main">
-			<header>
-				<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
-				<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
-				<span class="username">@{{ pAcct }}</span>
-				<div class="info">
-					<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
-					<router-link class="created-at" :to="url">
-						<mk-time :time="p.createdAt"/>
-					</router-link>
-				</div>
-			</header>
-			<div class="body">
-				<p class="channel" v-if="p.channel != null"><a target="_blank">{{ p.channel.title }}</a>:</p>
-				<div class="text">
-					<a class="reply" v-if="p.reply">
-						%fa:reply%
-					</a>
-					<mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
-					<a class="rp" v-if="p.renote != null">RP:</a>
-				</div>
-				<div class="media" v-if="p.media.length > 0">
-					<mk-media-list :media-list="p.media"/>
-				</div>
-				<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
-				<div class="tags" v-if="p.tags && p.tags.length > 0">
-					<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
-				</div>
-				<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
-				<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
-				<div class="map" v-if="p.geo" ref="map"></div>
-				<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
-				<div class="renote" v-if="p.renote">
-					<mk-note-preview :note="p.renote"/>
-				</div>
-			</div>
-			<footer>
-				<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
-				<button @click="reply">
-					%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
-				</button>
-				<button @click="renote" title="Renote">
-					%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
-				</button>
-				<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
-					%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
-				</button>
-				<button class="menu" @click="menu" ref="menuButton">
-					%fa:ellipsis-h%
-				</button>
-			</footer>
-		</div>
-	</article>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../acct/render';
-import getUserName from '../../../../../renderers/get-user-name';
-import parse from '../../../../../text/parse';
-
-import MkNoteMenu from '../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './note.sub.vue';
-
-export default Vue.extend({
-	components: {
-		XSub
-	},
-
-	props: ['note'],
-
-	data() {
-		return {
-			connection: null,
-			connectionId: null
-		};
-	},
-
-	computed: {
-		acct(): string {
-			return getAcct(this.note.user);
-		},
-		name(): string {
-			return getUserName(this.note.user);
-		},
-		pAcct(): string {
-			return getAcct(this.p.user);
-		},
-		pName(): string {
-			return getUserName(this.p.user);
-		},
-		isRenote(): boolean {
-			return (this.note.renote &&
-				this.note.text == null &&
-				this.note.mediaIds == null &&
-				this.note.poll == null);
-		},
-		p(): any {
-			return this.isRenote ? this.note.renote : this.note;
-		},
-		reactionsCount(): number {
-			return this.p.reactionCounts
-				? Object.keys(this.p.reactionCounts)
-					.map(key => this.p.reactionCounts[key])
-					.reduce((a, b) => a + b)
-				: 0;
-		},
-		url(): string {
-			return `/@${this.pAcct}/${this.p.id}`;
-		},
-		urls(): string[] {
-			if (this.p.text) {
-				const ast = parse(this.p.text);
-				return ast
-					.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
-					.map(t => t.url);
-			} else {
-				return null;
-			}
-		}
-	},
-
-	created() {
-		if ((this as any).os.isSignedIn) {
-			this.connection = (this as any).os.stream.getConnection();
-			this.connectionId = (this as any).os.stream.use();
-		}
-	},
-
-	mounted() {
-		this.capture(true);
-
-		if ((this as any).os.isSignedIn) {
-			this.connection.on('_connected_', this.onStreamConnected);
-		}
-
-		// Draw map
-		if (this.p.geo) {
-			const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
-			if (shouldShowMap) {
-				(this as any).os.getGoogleMaps().then(maps => {
-					const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
-					const map = new maps.Map(this.$refs.map, {
-						center: uluru,
-						zoom: 15
-					});
-					new maps.Marker({
-						position: uluru,
-						map: map
-					});
-				});
-			}
-		}
-	},
-
-	beforeDestroy() {
-		this.decapture(true);
-
-		if ((this as any).os.isSignedIn) {
-			this.connection.off('_connected_', this.onStreamConnected);
-			(this as any).os.stream.dispose(this.connectionId);
-		}
-	},
-
-	methods: {
-		capture(withHandler = false) {
-			if ((this as any).os.isSignedIn) {
-				this.connection.send({
-					type: 'capture',
-					id: this.p.id
-				});
-				if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
-			}
-		},
-		decapture(withHandler = false) {
-			if ((this as any).os.isSignedIn) {
-				this.connection.send({
-					type: 'decapture',
-					id: this.p.id
-				});
-				if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
-			}
-		},
-		onStreamConnected() {
-			this.capture();
-		},
-		onStreamNoteUpdated(data) {
-			const note = data.note;
-			if (note.id == this.note.id) {
-				this.$emit('update:note', note);
-			} else if (note.id == this.note.renoteId) {
-				this.note.renote = note;
-			}
-		},
-		reply() {
-			(this as any).apis.post({
-				reply: this.p
-			});
-		},
-		renote() {
-			(this as any).apis.post({
-				renote: this.p
-			});
-		},
-		react() {
-			(this as any).os.new(MkReactionPicker, {
-				source: this.$refs.reactButton,
-				note: this.p,
-				compact: true
-			});
-		},
-		menu() {
-			(this as any).os.new(MkNoteMenu, {
-				source: this.$refs.menuButton,
-				note: this.p,
-				compact: true
-			});
-		}
-	}
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.note
-	font-size 12px
-	border-bottom solid 1px #eaeaea
-
-	&:first-child
-		border-radius 8px 8px 0 0
-
-		> .renote
-			border-radius 8px 8px 0 0
-
-	&:last-of-type
-		border-bottom none
-
-	@media (min-width 350px)
-		font-size 14px
-
-	@media (min-width 500px)
-		font-size 16px
-
-	> .renote
-		color #9dbb00
-		background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
-
-		> p
-			margin 0
-			padding 8px 16px
-			line-height 28px
-
-			@media (min-width 500px)
-				padding 16px
-
-			.avatar-anchor
-				display inline-block
-
-				.avatar
-					vertical-align bottom
-					width 28px
-					height 28px
-					margin 0 8px 0 0
-					border-radius 6px
-
-			[data-fa]
-				margin-right 4px
-
-			.name
-				font-weight bold
-
-		> .mk-time
-			position absolute
-			top 8px
-			right 16px
-			font-size 0.9em
-			line-height 28px
-
-			@media (min-width 500px)
-				top 16px
-
-		& + article
-			padding-top 8px
-
-	> .reply-to
-		background rgba(0, 0, 0, 0.0125)
-
-		> .mk-note-preview
-			background transparent
-
-	> article
-		padding 14px 16px 9px 16px
-
-		&:after
-			content ""
-			display block
-			clear both
-
-		> .avatar-anchor
-			display block
-			float left
-			margin 0 10px 8px 0
-			position -webkit-sticky
-			position sticky
-			top 62px
-
-			@media (min-width 500px)
-				margin-right 16px
-
-			> .avatar
-				display block
-				width 48px
-				height 48px
-				margin 0
-				border-radius 6px
-				vertical-align bottom
-
-				@media (min-width 500px)
-					width 58px
-					height 58px
-					border-radius 8px
-
-		> .main
-			float left
-			width calc(100% - 58px)
-
-			@media (min-width 500px)
-				width calc(100% - 74px)
-
-			> header
-				display flex
-				align-items center
-				white-space nowrap
-
-				@media (min-width 500px)
-					margin-bottom 2px
-
-				> .name
-					display block
-					margin 0 0.5em 0 0
-					padding 0
-					overflow hidden
-					color #627079
-					font-size 1em
-					font-weight bold
-					text-decoration none
-					text-overflow ellipsis
-
-					&:hover
-						text-decoration underline
-
-				> .is-bot
-					margin 0 0.5em 0 0
-					padding 1px 6px
-					font-size 12px
-					color #aaa
-					border solid 1px #ddd
-					border-radius 3px
-
-				> .username
-					margin 0 0.5em 0 0
-					color #ccc
-
-				> .info
-					margin-left auto
-					font-size 0.9em
-
-					> .mobile
-						margin-right 6px
-						color #c0c0c0
-
-					> .created-at
-						color #c0c0c0
-
-			> .body
-
-				> .text
-					display block
-					margin 0
-					padding 0
-					overflow-wrap break-word
-					font-size 1.1em
-					color #717171
-
-					>>> .quote
-						margin 8px
-						padding 6px 12px
-						color #aaa
-						border-left solid 3px #eee
-
-					> .reply
-						margin-right 8px
-						color #717171
-
-					> .rp
-						margin-left 4px
-						font-style oblique
-						color #a0bf46
-
-					[data-is-me]:after
-						content "you"
-						padding 0 4px
-						margin-left 4px
-						font-size 80%
-						color $theme-color-foreground
-						background $theme-color
-						border-radius 4px
-
-				.mk-url-preview
-					margin-top 8px
-
-				> .channel
-					margin 0
-
-				> .tags
-					margin 4px 0 0 0
-
-					> *
-						display inline-block
-						margin 0 8px 0 0
-						padding 2px 8px 2px 16px
-						font-size 90%
-						color #8d969e
-						background #edf0f3
-						border-radius 4px
-
-						&:before
-							content ""
-							display block
-							position absolute
-							top 0
-							bottom 0
-							left 4px
-							width 8px
-							height 8px
-							margin auto 0
-							background #fff
-							border-radius 100%
-
-				> .media
-					> img
-						display block
-						max-width 100%
-
-				> .location
-					margin 4px 0
-					font-size 12px
-					color #ccc
-
-				> .map
-					width 100%
-					height 200px
-
-					&:empty
-						display none
-
-				> .app
-					font-size 12px
-					color #ccc
-
-				> .mk-poll
-					font-size 80%
-
-				> .renote
-					margin 8px 0
-
-					> .mk-note-preview
-						padding 16px
-						border dashed 1px #c0dac6
-						border-radius 8px
-
-			> footer
-				> button
-					margin 0
-					padding 8px
-					background transparent
-					border none
-					box-shadow none
-					font-size 1em
-					color #ddd
-					cursor pointer
-
-					&:not(:last-child)
-						margin-right 28px
-
-					&:hover
-						color #666
-
-					> .count
-						display inline
-						margin 0 0 0 8px
-						color #999
-
-					&.reacted
-						color $theme-color
-
-					&.menu
-						@media (max-width 350px)
-							display none
-
-</style>
-
-<style lang="stylus" module>
-.text
-	code
-		padding 4px 8px
-		margin 0 0.5em
-		font-size 80%
-		color #525252
-		background #f8f8f8
-		border-radius 2px
-
-	pre > code
-		padding 16px
-		margin 0
-</style>
diff --git a/src/client/app/mobile/views/components/ui.header.vue b/src/client/app/mobile/views/components/ui.header.vue
index fd4f31fd98..f664341cdd 100644
--- a/src/client/app/mobile/views/components/ui.header.vue
+++ b/src/client/app/mobile/views/components/ui.header.vue
@@ -63,9 +63,9 @@ export default Vue.extend({
 				}
 			});
 
-			const ago = (new Date().getTime() - new Date((this as any).os.i.account.lastUsedAt).getTime()) / 1000
+			const ago = (new Date().getTime() - new Date((this as any).os.i.lastUsedAt).getTime()) / 1000
 			const isHisasiburi = ago >= 3600;
-			(this as any).os.i.account.lastUsedAt = new Date();
+			(this as any).os.i.lastUsedAt = new Date();
 			if (isHisasiburi) {
 				(this.$refs.welcomeback as any).style.display = 'block';
 				(this.$refs.main as any).style.overflow = 'hidden';
diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue
index ab61166cf3..3de2ba1c3e 100644
--- a/src/client/app/mobile/views/pages/home.vue
+++ b/src/client/app/mobile/views/pages/home.vue
@@ -82,8 +82,8 @@ export default Vue.extend({
 		};
 	},
 	created() {
-		if ((this as any).os.i.account.clientSettings.mobileHome == null) {
-			Vue.set((this as any).os.i.account.clientSettings, 'mobileHome', [{
+		if ((this as any).os.i.clientSettings.mobileHome == null) {
+			Vue.set((this as any).os.i.clientSettings, 'mobileHome', [{
 				name: 'calendar',
 				id: 'a', data: {}
 			}, {
@@ -105,14 +105,14 @@ export default Vue.extend({
 				name: 'version',
 				id: 'g', data: {}
 			}]);
-			this.widgets = (this as any).os.i.account.clientSettings.mobileHome;
+			this.widgets = (this as any).os.i.clientSettings.mobileHome;
 			this.saveHome();
 		} else {
-			this.widgets = (this as any).os.i.account.clientSettings.mobileHome;
+			this.widgets = (this as any).os.i.clientSettings.mobileHome;
 		}
 
-		this.$watch('os.i.account.clientSettings', i => {
-			this.widgets = (this as any).os.i.account.clientSettings.mobileHome;
+		this.$watch('os.i.clientSettings', i => {
+			this.widgets = (this as any).os.i.clientSettings.mobileHome;
 		}, {
 			deep: true
 		});
@@ -157,15 +157,15 @@ export default Vue.extend({
 		},
 		onHomeUpdated(data) {
 			if (data.home) {
-				(this as any).os.i.account.clientSettings.mobileHome = data.home;
+				(this as any).os.i.clientSettings.mobileHome = data.home;
 				this.widgets = data.home;
 			} else {
-				const w = (this as any).os.i.account.clientSettings.mobileHome.find(w => w.id == data.id);
+				const w = (this as any).os.i.clientSettings.mobileHome.find(w => w.id == data.id);
 				if (w != null) {
 					w.data = data.data;
 					this.$refs[w.id][0].preventSave = true;
 					this.$refs[w.id][0].props = w.data;
-					this.widgets = (this as any).os.i.account.clientSettings.mobileHome;
+					this.widgets = (this as any).os.i.clientSettings.mobileHome;
 				}
 			}
 		},
@@ -194,7 +194,7 @@ export default Vue.extend({
 			this.saveHome();
 		},
 		saveHome() {
-			(this as any).os.i.account.clientSettings.mobileHome = this.widgets;
+			(this as any).os.i.clientSettings.mobileHome = this.widgets;
 			(this as any).api('i/update_mobile_home', {
 				home: this.widgets
 			});
diff --git a/src/client/app/mobile/views/pages/profile-setting.vue b/src/client/app/mobile/views/pages/profile-setting.vue
index 4a560c0272..7f0ff5aad7 100644
--- a/src/client/app/mobile/views/pages/profile-setting.vue
+++ b/src/client/app/mobile/views/pages/profile-setting.vue
@@ -53,9 +53,9 @@ export default Vue.extend({
 	},
 	created() {
 		this.name = (this as any).os.i.name || '';
-		this.location = (this as any).os.i.account.profile.location;
+		this.location = (this as any).os.i.profile.location;
 		this.description = (this as any).os.i.description;
-		this.birthday = (this as any).os.i.account.profile.birthday;
+		this.birthday = (this as any).os.i.profile.birthday;
 	},
 	mounted() {
 		document.title = 'Misskey | %i18n:mobile.tags.mk-profile-setting-page.title%';
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index 08fa4e2774..f33f209db1 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -18,11 +18,11 @@
 				</div>
 				<div class="description">{{ user.description }}</div>
 				<div class="info">
-					<p class="location" v-if="user.host === null && user.account.profile.location">
-						%fa:map-marker%{{ user.account.profile.location }}
+					<p class="location" v-if="user.host === null && user.profile.location">
+						%fa:map-marker%{{ user.profile.location }}
 					</p>
-					<p class="birthday" v-if="user.host === null && user.account.profile.birthday">
-						%fa:birthday-cake%{{ user.account.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)
+					<p class="birthday" v-if="user.host === null && user.profile.birthday">
+						%fa:birthday-cake%{{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)
 					</p>
 				</div>
 				<div class="status">
@@ -81,7 +81,7 @@ export default Vue.extend({
 			return this.getAcct(this.user);
 		},
 		age(): number {
-			return age(this.user.account.profile.birthday);
+			return age(this.user.profile.birthday);
 		},
 		name() {
 			return getUserName(this.user);
diff --git a/src/client/app/mobile/views/pages/user/home.vue b/src/client/app/mobile/views/pages/user/home.vue
index 2554084969..c0cd9b8da8 100644
--- a/src/client/app/mobile/views/pages/user/home.vue
+++ b/src/client/app/mobile/views/pages/user/home.vue
@@ -31,7 +31,7 @@
 			<x-followers-you-know :user="user"/>
 		</div>
 	</section>
-	<p v-if="user.host === null">%i18n:mobile.tags.mk-user-overview.last-used-at%: <b><mk-time :time="user.account.lastUsedAt"/></b></p>
+	<p v-if="user.host === null">%i18n:mobile.tags.mk-user-overview.last-used-at%: <b><mk-time :time="user.lastUsedAt"/></b></p>
 </div>
 </template>
 
diff --git a/src/client/app/mobile/views/pages/welcome.vue b/src/client/app/mobile/views/pages/welcome.vue
index 17cdf93065..27baf8bee4 100644
--- a/src/client/app/mobile/views/pages/welcome.vue
+++ b/src/client/app/mobile/views/pages/welcome.vue
@@ -8,7 +8,7 @@
 			<form @submit.prevent="onSubmit">
 				<input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange"/>
 				<input v-model="password" type="password" placeholder="パスワード" required/>
-				<input v-if="user && user.account.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
+				<input v-if="user && user.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
 				<button type="submit" :disabled="signing">{{ signing ? 'ログインしています' : 'ログイン' }}</button>
 			</form>
 			<div>
@@ -70,7 +70,7 @@ export default Vue.extend({
 			(this as any).api('signin', {
 				username: this.username,
 				password: this.password,
-				token: this.user && this.user.account.twoFactorEnabled ? this.token : undefined
+				token: this.user && this.user.twoFactorEnabled ? this.token : undefined
 			}).then(() => {
 				location.reload();
 			}).catch(() => {
diff --git a/src/models/user.ts b/src/models/user.ts
index f86aefe9a4..906bcb533b 100644
--- a/src/models/user.ts
+++ b/src/models/user.ts
@@ -11,7 +11,7 @@ import config from '../config';
 const User = db.get<IUser>('users');
 
 User.createIndex('username');
-User.createIndex('account.token');
+User.createIndex('token');
 
 export default User;
 
@@ -40,45 +40,41 @@ type IUserBase = {
 
 export interface ILocalUser extends IUserBase {
 	host: null;
-	account: {
-		keypair: string;
-		email: string;
-		links: string[];
-		password: string;
-		token: string;
-		twitter: {
-			accessToken: string;
-			accessTokenSecret: string;
-			userId: string;
-			screenName: string;
-		};
-		line: {
-			userId: string;
-		};
-		profile: {
-			location: string;
-			birthday: string; // 'YYYY-MM-DD'
-			tags: string[];
-		};
-		lastUsedAt: Date;
-		isBot: boolean;
-		isPro: boolean;
-		twoFactorSecret: string;
-		twoFactorEnabled: boolean;
-		twoFactorTempSecret: string;
-		clientSettings: any;
-		settings: any;
+	keypair: string;
+	email: string;
+	links: string[];
+	password: string;
+	token: string;
+	twitter: {
+		accessToken: string;
+		accessTokenSecret: string;
+		userId: string;
+		screenName: string;
 	};
+	line: {
+		userId: string;
+	};
+	profile: {
+		location: string;
+		birthday: string; // 'YYYY-MM-DD'
+		tags: string[];
+	};
+	lastUsedAt: Date;
+	isBot: boolean;
+	isPro: boolean;
+	twoFactorSecret: string;
+	twoFactorEnabled: boolean;
+	twoFactorTempSecret: string;
+	clientSettings: any;
+	settings: any;
 }
 
 export interface IRemoteUser extends IUserBase {
-	account: {
-		inbox: string;
-		uri: string;
-		publicKey: {
-			id: string;
-			publicKeyPem: string;
-		};
+	inbox: string;
+	uri: string;
+	publicKey: {
+		id: string;
+		publicKeyPem: string;
 	};
 }
 
@@ -150,11 +146,11 @@ export const pack = (
 
 	const fields = opts.detail ? {
 	} : {
-		'account.settings': false,
-		'account.clientSettings': false,
-		'account.profile': false,
-		'account.keywords': false,
-		'account.domains': false
+		settings: false,
+		clientSettings: false,
+		profile: false,
+		keywords: false,
+		domains: false
 	};
 
 	// Populate the user if 'user' is ID
@@ -188,29 +184,29 @@ export const pack = (
 	// Remove needless properties
 	delete _user.latestNote;
 
-	if (!_user.host) {
+	if (_user.host == null) {
 		// Remove private properties
-		delete _user.account.keypair;
-		delete _user.account.password;
-		delete _user.account.token;
-		delete _user.account.twoFactorTempSecret;
-		delete _user.account.twoFactorSecret;
+		delete _user.keypair;
+		delete _user.password;
+		delete _user.token;
+		delete _user.twoFactorTempSecret;
+		delete _user.twoFactorSecret;
 		delete _user.usernameLower;
-		if (_user.account.twitter) {
-			delete _user.account.twitter.accessToken;
-			delete _user.account.twitter.accessTokenSecret;
+		if (_user.twitter) {
+			delete _user.twitter.accessToken;
+			delete _user.twitter.accessTokenSecret;
 		}
-		delete _user.account.line;
+		delete _user.line;
 
 		// Visible via only the official client
 		if (!opts.includeSecrets) {
-			delete _user.account.email;
-			delete _user.account.settings;
-			delete _user.account.clientSettings;
+			delete _user.email;
+			delete _user.settings;
+			delete _user.clientSettings;
 		}
 
 		if (!opts.detail) {
-			delete _user.account.twoFactorEnabled;
+			delete _user.twoFactorEnabled;
 		}
 	}
 
diff --git a/src/queue/processors/http/process-inbox.ts b/src/queue/processors/http/process-inbox.ts
index eb4b62d37f..6608907a7a 100644
--- a/src/queue/processors/http/process-inbox.ts
+++ b/src/queue/processors/http/process-inbox.ts
@@ -36,7 +36,7 @@ export default async (job: kue.Job, done): Promise<void> => {
 	} else {
 		user = await User.findOne({
 			host: { $ne: null },
-			'account.publicKey.id': signature.keyId
+			'publicKey.id': signature.keyId
 		}) as IRemoteUser;
 
 		// アクティビティを送信してきたユーザーがまだMisskeyサーバーに登録されていなかったら登録する
@@ -50,7 +50,7 @@ export default async (job: kue.Job, done): Promise<void> => {
 		return;
 	}
 
-	if (!verifySignature(signature, user.account.publicKey.publicKeyPem)) {
+	if (!verifySignature(signature, user.publicKey.publicKeyPem)) {
 		console.warn('signature verification failed');
 		done();
 		return;
diff --git a/src/remote/activitypub/act/create/image.ts b/src/remote/activitypub/act/create/image.ts
index 30a75e7377..c87423c5fd 100644
--- a/src/remote/activitypub/act/create/image.ts
+++ b/src/remote/activitypub/act/create/image.ts
@@ -7,7 +7,7 @@ import { IDriveFile } from '../../../../models/drive-file';
 const log = debug('misskey:activitypub');
 
 export default async function(actor: IRemoteUser, image): Promise<IDriveFile> {
-	if ('attributedTo' in image && actor.account.uri !== image.attributedTo) {
+	if ('attributedTo' in image && actor.uri !== image.attributedTo) {
 		log(`invalid image: ${JSON.stringify(image, null, 2)}`);
 		throw new Error('invalid image');
 	}
diff --git a/src/remote/activitypub/act/create/index.ts b/src/remote/activitypub/act/create/index.ts
index dd0b112141..7cb9b08449 100644
--- a/src/remote/activitypub/act/create/index.ts
+++ b/src/remote/activitypub/act/create/index.ts
@@ -9,7 +9,7 @@ import { ICreate } from '../../type';
 const log = debug('misskey:activitypub');
 
 export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
-	if ('actor' in activity && actor.account.uri !== activity.actor) {
+	if ('actor' in activity && actor.uri !== activity.actor) {
 		throw new Error('invalid actor');
 	}
 
diff --git a/src/remote/activitypub/act/delete/index.ts b/src/remote/activitypub/act/delete/index.ts
index 6c6faa1ae5..10b47dc4ca 100644
--- a/src/remote/activitypub/act/delete/index.ts
+++ b/src/remote/activitypub/act/delete/index.ts
@@ -7,7 +7,7 @@ import { IRemoteUser } from '../../../../models/user';
  * 削除アクティビティを捌きます
  */
 export default async (actor: IRemoteUser, activity): Promise<void> => {
-	if ('actor' in activity && actor.account.uri !== activity.actor) {
+	if ('actor' in activity && actor.uri !== activity.actor) {
 		throw new Error('invalid actor');
 	}
 
diff --git a/src/remote/activitypub/act/undo/index.ts b/src/remote/activitypub/act/undo/index.ts
index 3ede9fcfb8..71f547aeb9 100644
--- a/src/remote/activitypub/act/undo/index.ts
+++ b/src/remote/activitypub/act/undo/index.ts
@@ -8,7 +8,7 @@ import Resolver from '../../resolver';
 const log = debug('misskey:activitypub');
 
 export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
-	if ('actor' in activity && actor.account.uri !== activity.actor) {
+	if ('actor' in activity && actor.uri !== activity.actor) {
 		throw new Error('invalid actor');
 	}
 
diff --git a/src/remote/activitypub/renderer/follow.ts b/src/remote/activitypub/renderer/follow.ts
index 6d1ded9a95..0a1ae1a4b2 100644
--- a/src/remote/activitypub/renderer/follow.ts
+++ b/src/remote/activitypub/renderer/follow.ts
@@ -4,5 +4,5 @@ import { IRemoteUser } from '../../../models/user';
 export default ({ username }, followee: IRemoteUser) => ({
 	type: 'Follow',
 	actor: `${config.url}/@${username}`,
-	object: followee.account.uri
+	object: followee.uri
 });
diff --git a/src/remote/activitypub/renderer/key.ts b/src/remote/activitypub/renderer/key.ts
index 85be7b1367..76e2f13bcc 100644
--- a/src/remote/activitypub/renderer/key.ts
+++ b/src/remote/activitypub/renderer/key.ts
@@ -6,5 +6,5 @@ export default (user: ILocalUser) => ({
 	id: `${config.url}/@${user.username}/publickey`,
 	type: 'Key',
 	owner: `${config.url}/@${user.username}`,
-	publicKeyPem: extractPublic(user.account.keypair)
+	publicKeyPem: extractPublic(user.keypair)
 });
diff --git a/src/remote/request.ts b/src/remote/request.ts
index a375aebfbb..a0c69cf4ef 100644
--- a/src/remote/request.ts
+++ b/src/remote/request.ts
@@ -4,10 +4,11 @@ import { URL } from 'url';
 import * as debug from 'debug';
 
 import config from '../config';
+import { ILocalUser } from '../models/user';
 
 const log = debug('misskey:activitypub:deliver');
 
-export default ({ account, username }, url, object) => new Promise((resolve, reject) => {
+export default (user: ILocalUser, url, object) => new Promise((resolve, reject) => {
 	log(`--> ${url}`);
 
 	const { protocol, hostname, port, pathname, search } = new URL(url);
@@ -35,8 +36,8 @@ export default ({ account, username }, url, object) => new Promise((resolve, rej
 
 	sign(req, {
 		authorizationHeaderName: 'Signature',
-		key: account.keypair,
-		keyId: `acct:${username}@${config.host}`
+		key: user.keypair,
+		keyId: `acct:${user.username}@${config.host}`
 	});
 
 	req.end(JSON.stringify(object));
diff --git a/src/renderers/get-user-summary.ts b/src/renderers/get-user-summary.ts
index 52309954d3..1bd9a7fb47 100644
--- a/src/renderers/get-user-summary.ts
+++ b/src/renderers/get-user-summary.ts
@@ -11,8 +11,7 @@ export default function(user: IUser): string {
 		`${user.notesCount}投稿、${user.followingCount}フォロー、${user.followersCount}フォロワー\n`;
 
 	if (isLocalUser(user)) {
-		const account = user.account;
-		string += `場所: ${account.profile.location}、誕生日: ${account.profile.birthday}\n`;
+		string += `場所: ${user.profile.location}、誕生日: ${user.profile.birthday}\n`;
 	}
 
 	return string + `「${user.description}」`;
diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts
index 8566744831..adbeeb3b34 100644
--- a/src/server/api/authenticate.ts
+++ b/src/server/api/authenticate.ts
@@ -34,7 +34,7 @@ export default (req: express.Request) => new Promise<IAuthContext>(async (resolv
 
 	if (isNativeToken(token)) {
 		const user: IUser = await User
-			.findOne({ 'account.token': token });
+			.findOne({ 'token': token });
 
 		if (user === null) {
 			return reject('user not found');
diff --git a/src/server/api/bot/core.ts b/src/server/api/bot/core.ts
index 1cf0522349..d41af48057 100644
--- a/src/server/api/bot/core.ts
+++ b/src/server/api/bot/core.ts
@@ -226,7 +226,7 @@ class SigninContext extends Context {
 			}
 		} else {
 			// Compare password
-			const same = await bcrypt.compare(query, this.temporaryUser.account.password);
+			const same = await bcrypt.compare(query, this.temporaryUser.password);
 
 			if (same) {
 				this.bot.signin(this.temporaryUser);
diff --git a/src/server/api/bot/interfaces/line.ts b/src/server/api/bot/interfaces/line.ts
index b6b0c257e9..be3bfe33d3 100644
--- a/src/server/api/bot/interfaces/line.ts
+++ b/src/server/api/bot/interfaces/line.ts
@@ -112,11 +112,11 @@ class LineBot extends BotCore {
 			data: `showtl|${user.id}`
 		});
 
-		if (user.account.twitter) {
+		if (user.twitter) {
 			actions.push({
 				type: 'uri',
 				label: 'Twitterアカウントを見る',
-				uri: `https://twitter.com/${user.account.twitter.screenName}`
+				uri: `https://twitter.com/${user.twitter.screenName}`
 			});
 		}
 
@@ -174,7 +174,7 @@ module.exports = async (app: express.Application) => {
 		if (session == null) {
 			const user = await User.findOne({
 				host: null,
-				'account.line': {
+				'line': {
 					userId: sourceId
 				}
 			});
@@ -184,7 +184,7 @@ module.exports = async (app: express.Application) => {
 			bot.on('signin', user => {
 				User.update(user._id, {
 					$set: {
-						'account.line': {
+						'line': {
 							userId: sourceId
 						}
 					}
@@ -194,7 +194,7 @@ module.exports = async (app: express.Application) => {
 			bot.on('signout', user => {
 				User.update(user._id, {
 					$set: {
-						'account.line': {
+						'line': {
 							userId: null
 						}
 					}
diff --git a/src/server/api/common/signin.ts b/src/server/api/common/signin.ts
index f9688790c4..8bb327694d 100644
--- a/src/server/api/common/signin.ts
+++ b/src/server/api/common/signin.ts
@@ -2,7 +2,7 @@ import config from '../../../config';
 
 export default function(res, user, redirect: boolean) {
 	const expires = 1000 * 60 * 60 * 24 * 365; // One Year
-	res.cookie('i', user.account.token, {
+	res.cookie('i', user.token, {
 		path: '/',
 		domain: `.${config.hostname}`,
 		secure: config.url.substr(0, 5) === 'https',
diff --git a/src/server/api/endpoints/following/create.ts b/src/server/api/endpoints/following/create.ts
index 0ccac8d83d..d3cc549ca7 100644
--- a/src/server/api/endpoints/following/create.ts
+++ b/src/server/api/endpoints/following/create.ts
@@ -31,7 +31,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	}, {
 		fields: {
 			data: false,
-			'account.profile': false
+			'profile': false
 		}
 	});
 
diff --git a/src/server/api/endpoints/following/delete.ts b/src/server/api/endpoints/following/delete.ts
index 0684b87504..0d0a6c7132 100644
--- a/src/server/api/endpoints/following/delete.ts
+++ b/src/server/api/endpoints/following/delete.ts
@@ -31,7 +31,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	}, {
 		fields: {
 			data: false,
-			'account.profile': false
+			'profile': false
 		}
 	});
 
diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts
index 44de71d162..0be30500c4 100644
--- a/src/server/api/endpoints/i.ts
+++ b/src/server/api/endpoints/i.ts
@@ -5,12 +5,6 @@ import User, { pack } from '../../../models/user';
 
 /**
  * Show myself
- *
- * @param {any} params
- * @param {any} user
- * @param {any} app
- * @param {Boolean} isSecure
- * @return {Promise<any>}
  */
 module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) => {
 	// Serialize
@@ -22,7 +16,7 @@ module.exports = (params, user, _, isSecure) => new Promise(async (res, rej) =>
 	// Update lastUsedAt
 	User.update({ _id: user._id }, {
 		$set: {
-			'account.lastUsedAt': new Date()
+			lastUsedAt: new Date()
 		}
 	});
 });
diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts
index 0b2e32c13f..3e824feffd 100644
--- a/src/server/api/endpoints/i/2fa/done.ts
+++ b/src/server/api/endpoints/i/2fa/done.ts
@@ -28,8 +28,8 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 
 	await User.update(user._id, {
 		$set: {
-			'account.twoFactorSecret': user.twoFactorTempSecret,
-			'account.twoFactorEnabled': true
+			'twoFactorSecret': user.twoFactorTempSecret,
+			'twoFactorEnabled': true
 		}
 	});
 
diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts
index dc7fb959bb..bed64a2545 100644
--- a/src/server/api/endpoints/i/2fa/register.ts
+++ b/src/server/api/endpoints/i/2fa/register.ts
@@ -14,7 +14,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (passwordErr) return rej('invalid password param');
 
 	// Compare password
-	const same = await bcrypt.compare(password, user.account.password);
+	const same = await bcrypt.compare(password, user.password);
 
 	if (!same) {
 		return rej('incorrect password');
diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts
index ff2a435fee..f9d7a25f53 100644
--- a/src/server/api/endpoints/i/2fa/unregister.ts
+++ b/src/server/api/endpoints/i/2fa/unregister.ts
@@ -11,7 +11,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (passwordErr) return rej('invalid password param');
 
 	// Compare password
-	const same = await bcrypt.compare(password, user.account.password);
+	const same = await bcrypt.compare(password, user.password);
 
 	if (!same) {
 		return rej('incorrect password');
@@ -19,8 +19,8 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 
 	await User.update(user._id, {
 		$set: {
-			'account.twoFactorSecret': null,
-			'account.twoFactorEnabled': false
+			'twoFactorSecret': null,
+			'twoFactorEnabled': false
 		}
 	});
 
diff --git a/src/server/api/endpoints/i/change_password.ts b/src/server/api/endpoints/i/change_password.ts
index a38b56a216..57415083f1 100644
--- a/src/server/api/endpoints/i/change_password.ts
+++ b/src/server/api/endpoints/i/change_password.ts
@@ -22,7 +22,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (newPasswordErr) return rej('invalid newPassword param');
 
 	// Compare password
-	const same = await bcrypt.compare(currentPassword, user.account.password);
+	const same = await bcrypt.compare(currentPassword, user.password);
 
 	if (!same) {
 		return rej('incorrect password');
@@ -34,7 +34,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 
 	await User.update(user._id, {
 		$set: {
-			'account.password': hash
+			'password': hash
 		}
 	});
 
diff --git a/src/server/api/endpoints/i/regenerate_token.ts b/src/server/api/endpoints/i/regenerate_token.ts
index 9aa6725f8c..f9e92c1797 100644
--- a/src/server/api/endpoints/i/regenerate_token.ts
+++ b/src/server/api/endpoints/i/regenerate_token.ts
@@ -20,7 +20,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (passwordErr) return rej('invalid password param');
 
 	// Compare password
-	const same = await bcrypt.compare(password, user.account.password);
+	const same = await bcrypt.compare(password, user.password);
 
 	if (!same) {
 		return rej('incorrect password');
@@ -31,7 +31,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 
 	await User.update(user._id, {
 		$set: {
-			'account.token': secret
+			'token': secret
 		}
 	});
 
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index 279b062f52..a8caa0ebc4 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -29,12 +29,12 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re
 	// Get 'location' parameter
 	const [location, locationErr] = $(params.location).optional.nullable.string().pipe(isValidLocation).$;
 	if (locationErr) return rej('invalid location param');
-	if (location !== undefined) user.account.profile.location = location;
+	if (location !== undefined) user.profile.location = location;
 
 	// Get 'birthday' parameter
 	const [birthday, birthdayErr] = $(params.birthday).optional.nullable.string().pipe(isValidBirthday).$;
 	if (birthdayErr) return rej('invalid birthday param');
-	if (birthday !== undefined) user.account.profile.birthday = birthday;
+	if (birthday !== undefined) user.profile.birthday = birthday;
 
 	// Get 'avatarId' parameter
 	const [avatarId, avatarIdErr] = $(params.avatarId).optional.id().$;
@@ -49,12 +49,12 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re
 	// Get 'isBot' parameter
 	const [isBot, isBotErr] = $(params.isBot).optional.boolean().$;
 	if (isBotErr) return rej('invalid isBot param');
-	if (isBot != null) user.account.isBot = isBot;
+	if (isBot != null) user.isBot = isBot;
 
 	// Get 'autoWatch' parameter
 	const [autoWatch, autoWatchErr] = $(params.autoWatch).optional.boolean().$;
 	if (autoWatchErr) return rej('invalid autoWatch param');
-	if (autoWatch != null) user.account.settings.autoWatch = autoWatch;
+	if (autoWatch != null) user.settings.autoWatch = autoWatch;
 
 	await User.update(user._id, {
 		$set: {
@@ -62,9 +62,9 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re
 			description: user.description,
 			avatarId: user.avatarId,
 			bannerId: user.bannerId,
-			'account.profile': user.account.profile,
-			'account.isBot': user.account.isBot,
-			'account.settings': user.account.settings
+			'profile': user.profile,
+			'isBot': user.isBot,
+			'settings': user.settings
 		}
 	});
 
diff --git a/src/server/api/endpoints/i/update_client_setting.ts b/src/server/api/endpoints/i/update_client_setting.ts
index 10741aceba..b0d5db5ec2 100644
--- a/src/server/api/endpoints/i/update_client_setting.ts
+++ b/src/server/api/endpoints/i/update_client_setting.ts
@@ -22,14 +22,14 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (valueErr) return rej('invalid value param');
 
 	const x = {};
-	x[`account.clientSettings.${name}`] = value;
+	x[`clientSettings.${name}`] = value;
 
 	await User.update(user._id, {
 		$set: x
 	});
 
 	// Serialize
-	user.account.clientSettings[name] = value;
+	user.clientSettings[name] = value;
 	const iObj = await pack(user, user, {
 		detail: true,
 		includeSecrets: true
diff --git a/src/server/api/endpoints/i/update_home.ts b/src/server/api/endpoints/i/update_home.ts
index 91be0714d7..ce7661ede0 100644
--- a/src/server/api/endpoints/i/update_home.ts
+++ b/src/server/api/endpoints/i/update_home.ts
@@ -26,7 +26,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (home) {
 		await User.update(user._id, {
 			$set: {
-				'account.clientSettings.home': home
+				'clientSettings.home': home
 			}
 		});
 
@@ -38,7 +38,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	} else {
 		if (id == null && data == null) return rej('you need to set id and data params if home param unset');
 
-		const _home = user.account.clientSettings.home;
+		const _home = user.clientSettings.home;
 		const widget = _home.find(w => w.id == id);
 
 		if (widget == null) return rej('widget not found');
@@ -47,7 +47,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 
 		await User.update(user._id, {
 			$set: {
-				'account.clientSettings.home': _home
+				'clientSettings.home': _home
 			}
 		});
 
diff --git a/src/server/api/endpoints/i/update_mobile_home.ts b/src/server/api/endpoints/i/update_mobile_home.ts
index 1efda120d5..b710e2f330 100644
--- a/src/server/api/endpoints/i/update_mobile_home.ts
+++ b/src/server/api/endpoints/i/update_mobile_home.ts
@@ -25,7 +25,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	if (home) {
 		await User.update(user._id, {
 			$set: {
-				'account.clientSettings.mobileHome': home
+				'clientSettings.mobileHome': home
 			}
 		});
 
@@ -37,7 +37,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 	} else {
 		if (id == null && data == null) return rej('you need to set id and data params if home param unset');
 
-		const _home = user.account.clientSettings.mobileHome || [];
+		const _home = user.clientSettings.mobileHome || [];
 		const widget = _home.find(w => w.id == id);
 
 		if (widget == null) return rej('widget not found');
@@ -46,7 +46,7 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
 
 		await User.update(user._id, {
 			$set: {
-				'account.clientSettings.mobileHome': _home
+				'clientSettings.mobileHome': _home
 			}
 		});
 
diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts
index a7fa5f7b4b..19894d07af 100644
--- a/src/server/api/endpoints/mute/create.ts
+++ b/src/server/api/endpoints/mute/create.ts
@@ -30,7 +30,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	}, {
 		fields: {
 			data: false,
-			'account.profile': false
+			'profile': false
 		}
 	});
 
diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts
index 687f010336..10096352ba 100644
--- a/src/server/api/endpoints/mute/delete.ts
+++ b/src/server/api/endpoints/mute/delete.ts
@@ -30,7 +30,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 	}, {
 		fields: {
 			data: false,
-			'account.profile': false
+			'profile': false
 		}
 	});
 
diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts
index 0e27f87ee2..fd4412ad35 100644
--- a/src/server/api/endpoints/notes/polls/vote.ts
+++ b/src/server/api/endpoints/notes/polls/vote.ts
@@ -100,7 +100,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 		});
 
 	// この投稿をWatchする
-	if (user.account.settings.autoWatch !== false) {
+	if (user.settings.autoWatch !== false) {
 		watch(user._id, note);
 	}
 });
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index 60483936fb..2de22da13e 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -32,7 +32,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 			},
 			$or: [
 				{
-					'account.lastUsedAt': {
+					'lastUsedAt': {
 						$gte: new Date(Date.now() - ms('7days'))
 					}
 				}, {
diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts
index e0bd67d1ca..d7c4832c95 100644
--- a/src/server/api/private/signin.ts
+++ b/src/server/api/private/signin.ts
@@ -37,7 +37,7 @@ export default async (req: express.Request, res: express.Response) => {
 	}, {
 		fields: {
 			data: false,
-			'account.profile': false
+			'profile': false
 		}
 	}) as ILocalUser;
 
@@ -48,15 +48,13 @@ export default async (req: express.Request, res: express.Response) => {
 		return;
 	}
 
-	const account = user.account;
-
 	// Compare password
-	const same = await bcrypt.compare(password, account.password);
+	const same = await bcrypt.compare(password, password);
 
 	if (same) {
-		if (account.twoFactorEnabled) {
+		if (user.twoFactorEnabled) {
 			const verified = (speakeasy as any).totp.verify({
-				secret: account.twoFactorSecret,
+				secret: user.twoFactorSecret,
 				encoding: 'base32',
 				token: token
 			});
diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts
index 5818ba25cd..f441e1b754 100644
--- a/src/server/api/private/signup.ts
+++ b/src/server/api/private/signup.ts
@@ -119,44 +119,29 @@ export default async (req: express.Request, res: express.Response) => {
 		usernameLower: username.toLowerCase(),
 		host: null,
 		hostLower: null,
-		account: {
-			keypair: generateKeypair(),
-			token: secret,
-			email: null,
-			links: null,
-			password: hash,
-			profile: {
-				bio: null,
-				birthday: null,
-				blood: null,
-				gender: null,
-				handedness: null,
-				height: null,
-				location: null,
-				weight: null
-			},
-			settings: {
-				autoWatch: true
-			},
-			clientSettings: {
-				home: homeData
-			}
+		keypair: generateKeypair(),
+		token: secret,
+		email: null,
+		links: null,
+		password: hash,
+		profile: {
+			bio: null,
+			birthday: null,
+			blood: null,
+			gender: null,
+			handedness: null,
+			height: null,
+			location: null,
+			weight: null
+		},
+		settings: {
+			autoWatch: true
+		},
+		clientSettings: {
+			home: homeData
 		}
 	});
 
 	// Response
 	res.send(await pack(account));
-
-	// Create search index
-	if (config.elasticsearch.enable) {
-		const es = require('../../db/elasticsearch');
-		es.index({
-			index: 'misskey',
-			type: 'user',
-			id: account._id.toString(),
-			body: {
-				username: username
-			}
-		});
-	}
 };
diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts
index 77b932b13b..da48e30a8a 100644
--- a/src/server/api/service/twitter.ts
+++ b/src/server/api/service/twitter.ts
@@ -40,10 +40,10 @@ module.exports = (app: express.Application) => {
 
 		const user = await User.findOneAndUpdate({
 			host: null,
-			'account.token': userToken
+			'token': userToken
 		}, {
 			$set: {
-				'account.twitter': null
+				'twitter': null
 			}
 		});
 
@@ -128,7 +128,7 @@ module.exports = (app: express.Application) => {
 
 				const user = await User.findOne({
 					host: null,
-					'account.twitter.userId': result.userId
+					'twitter.userId': result.userId
 				});
 
 				if (user == null) {
@@ -151,10 +151,10 @@ module.exports = (app: express.Application) => {
 
 				const user = await User.findOneAndUpdate({
 					host: null,
-					'account.token': userToken
+					'token': userToken
 				}, {
 					$set: {
-						'account.twitter': {
+						'twitter': {
 							accessToken: result.accessToken,
 							accessTokenSecret: result.accessTokenSecret,
 							userId: result.userId,
diff --git a/src/server/api/stream/home.ts b/src/server/api/stream/home.ts
index 313558851b..359ef74aff 100644
--- a/src/server/api/stream/home.ts
+++ b/src/server/api/stream/home.ts
@@ -74,7 +74,7 @@ export default async function(request: websocket.request, connection: websocket.
 				// Update lastUsedAt
 				User.update({ _id: user._id }, {
 					$set: {
-						'account.lastUsedAt': new Date()
+						'lastUsedAt': new Date()
 					}
 				});
 				break;
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index edcf505d24..26946b524e 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -97,7 +97,7 @@ function authenticate(token: string): Promise<IUser> {
 			const user: IUser = await User
 				.findOne({
 					host: null,
-					'account.token': token
+					'token': token
 				});
 
 			resolve(user);
diff --git a/src/services/following/create.ts b/src/services/following/create.ts
index d919f4487f..31e3be19ed 100644
--- a/src/services/following/create.ts
+++ b/src/services/following/create.ts
@@ -60,13 +60,13 @@ export default async function(follower: IUser, followee: IUser, activity?) {
 		const content = renderFollow(follower, followee);
 		content['@context'] = context;
 
-		deliver(follower, content, followee.account.inbox).save();
+		deliver(follower, content, followee.inbox).save();
 	}
 
 	if (isRemoteUser(follower) && isLocalUser(followee)) {
 		const content = renderAccept(activity);
 		content['@context'] = context;
 
-		deliver(followee, content, follower.account.inbox).save();
+		deliver(followee, content, follower.inbox).save();
 	}
 }
diff --git a/src/services/following/delete.ts b/src/services/following/delete.ts
index 364a4803b9..d79bf64f53 100644
--- a/src/services/following/delete.ts
+++ b/src/services/following/delete.ts
@@ -59,6 +59,6 @@ export default async function(follower: IUser, followee: IUser, activity?) {
 		const content = renderUndo(renderFollow(follower, followee));
 		content['@context'] = context;
 
-		deliver(follower, content, followee.account.inbox).save();
+		deliver(follower, content, followee.inbox).save();
 	}
 }
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 8eee8f44af..551d618569 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -78,7 +78,7 @@ export default async (user: IUser, data: {
 			host: user.host,
 			hostLower: user.hostLower,
 			account: isLocalUser(user) ? {} : {
-				inbox: user.account.inbox
+				inbox: user.inbox
 			}
 		}
 	};
@@ -133,7 +133,7 @@ export default async (user: IUser, data: {
 
 			// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
 			if (data.reply && isLocalUser(user) && isRemoteUser(data.reply._user)) {
-				deliver(user, content, data.reply._user.account.inbox).save();
+				deliver(user, content, data.reply._user.inbox).save();
 			}
 
 			Promise.all(followers.map(follower => {
@@ -145,7 +145,7 @@ export default async (user: IUser, data: {
 				} else {
 					// フォロワーがリモートユーザーかつ投稿者がローカルユーザーなら投稿を配信
 					if (isLocalUser(user)) {
-						deliver(user, content, follower.account.inbox).save();
+						deliver(user, content, follower.inbox).save();
 					}
 				}
 			}));
@@ -242,7 +242,7 @@ export default async (user: IUser, data: {
 		});
 
 		// この投稿をWatchする
-		if (isLocalUser(user) && user.account.settings.autoWatch !== false) {
+		if (isLocalUser(user) && user.settings.autoWatch !== false) {
 			watch(user._id, data.reply);
 		}
 
@@ -277,7 +277,7 @@ export default async (user: IUser, data: {
 		});
 
 		// この投稿をWatchする
-		if (isLocalUser(user) && user.account.settings.autoWatch !== false) {
+		if (isLocalUser(user) && user.settings.autoWatch !== false) {
 			watch(user._id, data.renote);
 		}
 
diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts
index d0ce65ee54..ea51b205d0 100644
--- a/src/services/note/reaction/create.ts
+++ b/src/services/note/reaction/create.ts
@@ -78,7 +78,7 @@ export default async (user: IUser, note: INote, reaction: string) => new Promise
 		});
 
 	// ユーザーがローカルユーザーかつ自動ウォッチ設定がオンならばこの投稿をWatchする
-	if (isLocalUser(user) && user.account.settings.autoWatch !== false) {
+	if (isLocalUser(user) && user.settings.autoWatch !== false) {
 		watch(user._id, note);
 	}
 
@@ -88,7 +88,7 @@ export default async (user: IUser, note: INote, reaction: string) => new Promise
 
 	// リアクターがローカルユーザーかつリアクション対象がリモートユーザーの投稿なら配送
 	if (isLocalUser(user) && isRemoteUser(note._user)) {
-		deliver(user, content, note._user.account.inbox).save();
+		deliver(user, content, note._user.inbox).save();
 	}
 	//#endregion
 });
diff --git a/test/api.ts b/test/api.ts
index 953c5aea0d..87bbb8ee16 100644
--- a/test/api.ts
+++ b/test/api.ts
@@ -32,7 +32,7 @@ const async = fn => (done) => {
 
 const request = (endpoint, params, me?) => new Promise<any>((ok, ng) => {
 	const auth = me ? {
-		i: me.account.token
+		i: me.token
 	} : {};
 
 	_chai.request(server)
@@ -157,10 +157,10 @@ describe('API', () => {
 			res.should.have.status(200);
 			res.body.should.be.a('object');
 			res.body.should.have.property('name').eql(myName);
-			res.body.should.have.nested.property('account.profile').a('object');
-			res.body.should.have.nested.property('account.profile.location').eql(myLocation);
-			res.body.should.have.nested.property('account.profile.birthday').eql(myBirthday);
-			res.body.should.have.nested.property('account.profile.gender').eql('female');
+			res.body.should.have.nested.property('profile').a('object');
+			res.body.should.have.nested.property('profile.location').eql(myLocation);
+			res.body.should.have.nested.property('profile.birthday').eql(myBirthday);
+			res.body.should.have.nested.property('profile.gender').eql('female');
 		}));
 
 		it('名前を空白にできない', async(async () => {
@@ -180,8 +180,8 @@ describe('API', () => {
 			}, me);
 			res.should.have.status(200);
 			res.body.should.be.a('object');
-			res.body.should.have.nested.property('account.profile').a('object');
-			res.body.should.have.nested.property('account.profile.birthday').eql(null);
+			res.body.should.have.nested.property('profile').a('object');
+			res.body.should.have.nested.property('profile.birthday').eql(null);
 		}));
 
 		it('不正な誕生日の形式で怒られる', async(async () => {
@@ -736,7 +736,7 @@ describe('API', () => {
 			const me = await insertSakurako();
 			const res = await _chai.request(server)
 				.post('/drive/files/create')
-				.field('i', me.account.token)
+				.field('i', me.token)
 				.attach('file', fs.readFileSync(__dirname + '/resources/Lenna.png'), 'Lenna.png');
 			res.should.have.status(200);
 			res.body.should.be.a('object');
diff --git a/tools/migration/nighthike/12.js b/tools/migration/nighthike/12.js
new file mode 100644
index 0000000000..f4b61e2ee8
--- /dev/null
+++ b/tools/migration/nighthike/12.js
@@ -0,0 +1,58 @@
+// for Node.js interpret
+
+const { default: User } = require('../../../built/models/user');
+const { generate } = require('../../../built/crypto_key');
+const { default: zip } = require('@prezzemolo/zip')
+
+const migrate = async (user) => {
+	const result = await User.update(user._id, {
+		$unset: {
+			account: ''
+		},
+		$set: {
+			host: null,
+			hostLower: null,
+			email: user.account.email,
+			links: user.account.links,
+			password: user.account.password,
+			token: user.account.token,
+			twitter: user.account.twitter,
+			line: user.account.line,
+			profile: user.account.profile,
+			lastUsedAt: user.account.lastUsedAt,
+			isBot: user.account.isBot,
+			isPro: user.account.isPro,
+			twoFactorSecret: user.account.twoFactorSecret,
+			twoFactorEnabled: user.account.twoFactorEnabled,
+			clientSettings: user.account.clientSettings,
+			settings: user.account.settings,
+			keypair: user.account.keypair
+		}
+	});
+	return result.ok === 1;
+}
+
+async function main() {
+	const count = await User.count({});
+
+	const dop = Number.parseInt(process.argv[2]) || 5
+	const idop = ((count - (count % dop)) / dop) + 1
+
+	return zip(
+		1,
+		async (time) => {
+			console.log(`${time} / ${idop}`)
+			const doc = await User.find({}, {
+				limit: dop, skip: time * dop
+			})
+			return Promise.all(doc.map(migrate))
+		},
+		idop
+	).then(a => {
+		const rv = []
+		a.forEach(e => rv.push(...e))
+		return rv
+	})
+}
+
+main().then(console.dir).catch(console.error)