From f0a29721c9fb10f97faf386bc9d6b1b2fad97895 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 7 Apr 2019 21:50:36 +0900 Subject: Use PostgreSQL instead of MongoDB (#4572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * Update note.ts * Update timeline.ts * Update core.ts * wip * Update generate-visibility-query.ts * wip * wip * wip * wip * wip * Update global-timeline.ts * wip * wip * wip * Update vote.ts * wip * wip * Update create.ts * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update files.ts * wip * wip * Update CONTRIBUTING.md * wip * wip * wip * wip * wip * wip * wip * wip * Update read-notification.ts * wip * wip * wip * wip * wip * wip * wip * Update cancel.ts * wip * wip * wip * Update show.ts * wip * wip * Update gen-id.ts * Update create.ts * Update id.ts * wip * wip * wip * wip * wip * wip * wip * Docker: Update files about Docker (#4599) * Docker: Use cache if files used by `yarn install` was not updated This patch reduces the number of times to installing node_modules. For example, `yarn install` step will be skipped when only ".config/default.yml" is updated. * Docker: Migrate MongoDB to Postgresql Misskey uses Postgresql as a database instead of Mongodb since version 11. * Docker: Uncomment about data persistence This patch will save a lot of databases. * wip * wip * wip * Update activitypub.ts * wip * wip * wip * Update logs.ts * wip * Update drive-file.ts * Update register.ts * wip * wip * Update mentions.ts * wip * wip * wip * Update recommendation.ts * wip * Update index.ts * wip * Update recommendation.ts * Doc: Update docker.ja.md and docker.en.md (#1) (#4608) Update how to set up misskey. * wip * :v: * wip * Update note.ts * Update postgre.ts * wip * wip * wip * wip * Update add-file.ts * wip * wip * wip * Clean up * Update logs.ts * wip * :pizza: * wip * Ad notes * wip * Update api-visibility.ts * Update note.ts * Update add-file.ts * tests * tests * Update postgre.ts * Update utils.ts * wip * wip * Refactor * wip * Refactor * wip * wip * Update show-users.ts * Update update-instance.ts * wip * Update feed.ts * Update outbox.ts * Update outbox.ts * Update user.ts * wip * Update list.ts * Update update-hashtag.ts * wip * Update update-hashtag.ts * Refactor * Update update.ts * wip * wip * :v: * clean up * docs * Update push.ts * wip * Update api.ts * wip * :v: * Update make-pagination-query.ts * :v: * Delete hashtags.ts * Update instances.ts * Update instances.ts * Update create.ts * Update search.ts * Update reversi-game.ts * Update signup.ts * Update user.ts * id * Update example.yml * :art: * objectid * fix * reversi * reversi * Fix bug of chart engine * Add test of chart engine * Improve test * Better testing * Improve chart engine * Refactor * Add test of chart engine * Refactor * Add chart test * Fix bug * コミットし忘れ * Refactoring * :v: * Add tests * Add test * Extarct note tests * Refactor * 存在しないユーザーにメンションできなくなっていた問題を修正 * Fix bug * Update update-meta.ts * Fix bug * Update mention.vue * Fix bug * Update meta.ts * Update CONTRIBUTING.md * Fix bug * Fix bug * Fix bug * Clean up * Clean up * Update notification.ts * Clean up * Add mute tests * Add test * Refactor * Add test * Fix test * Refactor * Refactor * Add tests * Update utils.ts * Update utils.ts * Fix test * Update package.json * Update update.ts * Update manifest.ts * Fix bug * Fix bug * Add test * :art: * Update endpoint permissions * Updaye permisison * Update person.ts #4299 * データベースと同期しないように * Fix bug * Fix bug * Update reversi-game.ts * Use a feature of Node v11.7.0 to extract a public key (#4644) * wip * wip * :v: * Refactoring #1540 * test * test * test * test * test * test * test * Fix bug * Fix test * :sushi: * wip * #4471 * Add test for #4335 * Refactor * Fix test * Add tests * :clock4: * Fix bug * Add test * Add test * rename * Fix bug --- src/server/api/endpoints/i/2fa/done.ts | 12 +- src/server/api/endpoints/i/2fa/register.ts | 8 +- src/server/api/endpoints/i/2fa/unregister.ts | 10 +- src/server/api/endpoints/i/authorized-apps.ts | 25 ++-- src/server/api/endpoints/i/change-password.ts | 10 +- .../i/clear-follow-request-notification.ts | 23 ---- src/server/api/endpoints/i/delete-account.ts | 29 +---- src/server/api/endpoints/i/export-blocking.ts | 2 - src/server/api/endpoints/i/export-following.ts | 2 - src/server/api/endpoints/i/export-mute.ts | 2 - src/server/api/endpoints/i/export-notes.ts | 2 - src/server/api/endpoints/i/export-user-lists.ts | 2 - src/server/api/endpoints/i/favorites.ts | 41 ++---- src/server/api/endpoints/i/import-following.ts | 19 ++- src/server/api/endpoints/i/import-user-lists.ts | 19 ++- src/server/api/endpoints/i/notifications.ts | 81 ++++-------- src/server/api/endpoints/i/pin.ts | 9 +- .../api/endpoints/i/read-all-messaging-messages.ts | 25 +--- .../api/endpoints/i/read-all-unread-notes.ts | 22 +--- src/server/api/endpoints/i/regenerate-token.ts | 12 +- src/server/api/endpoints/i/signin-history.ts | 35 ++---- src/server/api/endpoints/i/unpin.ts | 9 +- .../api/endpoints/i/update-client-setting.ts | 22 ++-- src/server/api/endpoints/i/update-email.ts | 24 ++-- src/server/api/endpoints/i/update-home.ts | 33 ----- src/server/api/endpoints/i/update-mobile-home.ts | 32 ----- src/server/api/endpoints/i/update-widget.ts | 88 ------------- src/server/api/endpoints/i/update.ts | 138 +++++++-------------- 28 files changed, 174 insertions(+), 562 deletions(-) delete mode 100644 src/server/api/endpoints/i/clear-follow-request-notification.ts delete mode 100644 src/server/api/endpoints/i/update-home.ts delete mode 100644 src/server/api/endpoints/i/update-mobile-home.ts delete mode 100644 src/server/api/endpoints/i/update-widget.ts (limited to 'src/server/api/endpoints/i') diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts index 556354c386..8ccb09b8b7 100644 --- a/src/server/api/endpoints/i/2fa/done.ts +++ b/src/server/api/endpoints/i/2fa/done.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as speakeasy from 'speakeasy'; -import User from '../../../../../models/user'; import define from '../../../define'; +import { Users } from '../../../../../models'; export const meta = { requireCredential: true, @@ -32,12 +32,8 @@ export default define(meta, async (ps, user) => { throw new Error('not verified'); } - await User.update(user._id, { - $set: { - 'twoFactorSecret': user.twoFactorTempSecret, - 'twoFactorEnabled': true - } + await Users.update(user.id, { + twoFactorSecret: user.twoFactorTempSecret, + twoFactorEnabled: true }); - - return; }); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index 302b51ec0b..5efe77900a 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -2,9 +2,9 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; -import User from '../../../../../models/user'; import config from '../../../../../config'; import define from '../../../define'; +import { Users } from '../../../../../models'; export const meta = { requireCredential: true, @@ -31,10 +31,8 @@ export default define(meta, async (ps, user) => { length: 32 }); - await User.update(user._id, { - $set: { - twoFactorTempSecret: secret.base32 - } + await Users.update(user.id, { + twoFactorTempSecret: secret.base32 }); // Get the data URL of the authenticator URL diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts index 37b2639198..fb3ecd4043 100644 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ b/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../../models/user'; import define from '../../../define'; +import { Users } from '../../../../../models'; export const meta = { requireCredential: true, @@ -23,11 +23,9 @@ export default define(meta, async (ps, user) => { throw new Error('incorrect password'); } - await User.update(user._id, { - $set: { - 'twoFactorSecret': null, - 'twoFactorEnabled': false - } + await Users.update(user.id, { + twoFactorSecret: null, + twoFactorEnabled: false }); return; diff --git a/src/server/api/endpoints/i/authorized-apps.ts b/src/server/api/endpoints/i/authorized-apps.ts index cb8be9ed97..ebf04fcb58 100644 --- a/src/server/api/endpoints/i/authorized-apps.ts +++ b/src/server/api/endpoints/i/authorized-apps.ts @@ -1,7 +1,6 @@ import $ from 'cafy'; -import AccessToken from '../../../../models/access-token'; -import { pack } from '../../../../models/app'; import define from '../../define'; +import { AccessTokens, Apps } from '../../../../models'; export const meta = { requireCredential: true, @@ -28,18 +27,18 @@ export const meta = { export default define(meta, async (ps, user) => { // Get tokens - const tokens = await AccessToken - .find({ - userId: user._id - }, { - limit: ps.limit, - skip: ps.offset, - sort: { - _id: ps.sort == 'asc' ? 1 : -1 - } - }); + const tokens = await AccessTokens.find({ + where: { + userId: user.id + }, + take: ps.limit, + skip: ps.offset, + order: { + id: ps.sort == 'asc' ? 1 : -1 + } + }); - return await Promise.all(tokens.map(token => pack(token.appId, user, { + return await Promise.all(tokens.map(token => Apps.pack(token.appId, user, { detail: true }))); }); diff --git a/src/server/api/endpoints/i/change-password.ts b/src/server/api/endpoints/i/change-password.ts index 8ab286b4bf..f8f977200f 100644 --- a/src/server/api/endpoints/i/change-password.ts +++ b/src/server/api/endpoints/i/change-password.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../models/user'; import define from '../../define'; +import { Users } from '../../../../models'; export const meta = { requireCredential: true, @@ -31,11 +31,7 @@ export default define(meta, async (ps, user) => { const salt = await bcrypt.genSalt(8); const hash = await bcrypt.hash(ps.newPassword, salt); - await User.update(user._id, { - $set: { - 'password': hash - } + await Users.update(user.id, { + password: hash }); - - return; }); diff --git a/src/server/api/endpoints/i/clear-follow-request-notification.ts b/src/server/api/endpoints/i/clear-follow-request-notification.ts deleted file mode 100644 index 38c6ec1cef..0000000000 --- a/src/server/api/endpoints/i/clear-follow-request-notification.ts +++ /dev/null @@ -1,23 +0,0 @@ -import User from '../../../../models/user'; -import define from '../../define'; - -export const meta = { - tags: ['account', 'following'], - - requireCredential: true, - - kind: 'account-write', - - params: { - } -}; - -export default define(meta, async (ps, user) => { - await User.update({ _id: user._id }, { - $set: { - pendingReceivedFollowRequestsCount: 0 - } - }); - - return; -}); diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts index fed38eab5a..5aff74e0cc 100644 --- a/src/server/api/endpoints/i/delete-account.ts +++ b/src/server/api/endpoints/i/delete-account.ts @@ -1,10 +1,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../models/user'; import define from '../../define'; -import { createDeleteNotesJob, createDeleteDriveFilesJob } from '../../../../queue'; -import Message from '../../../../models/messaging-message'; -import Signin from '../../../../models/signin'; +import { Users } from '../../../../models'; export const meta = { requireCredential: true, @@ -26,27 +23,5 @@ export default define(meta, async (ps, user) => { throw new Error('incorrect password'); } - await User.update({ _id: user._id }, { - $set: { - isDeleted: true, - name: null, - description: null, - pinnedNoteIds: [], - password: null, - email: null, - twitter: null, - github: null, - discord: null, - profile: {}, - fields: [], - clientSettings: {}, - } - }); - - Message.remove({ userId: user._id }); - Signin.remove({ userId: user._id }); - createDeleteNotesJob(user); - createDeleteDriveFilesJob(user); - - return; + await Users.delete(user.id); }); diff --git a/src/server/api/endpoints/i/export-blocking.ts b/src/server/api/endpoints/i/export-blocking.ts index 346b29c79d..14d49487e8 100644 --- a/src/server/api/endpoints/i/export-blocking.ts +++ b/src/server/api/endpoints/i/export-blocking.ts @@ -13,6 +13,4 @@ export const meta = { export default define(meta, async (ps, user) => { createExportBlockingJob(user); - - return; }); diff --git a/src/server/api/endpoints/i/export-following.ts b/src/server/api/endpoints/i/export-following.ts index 5977b03105..50dd28837f 100644 --- a/src/server/api/endpoints/i/export-following.ts +++ b/src/server/api/endpoints/i/export-following.ts @@ -13,6 +13,4 @@ export const meta = { export default define(meta, async (ps, user) => { createExportFollowingJob(user); - - return; }); diff --git a/src/server/api/endpoints/i/export-mute.ts b/src/server/api/endpoints/i/export-mute.ts index 22ceff3631..1eb51cd77e 100644 --- a/src/server/api/endpoints/i/export-mute.ts +++ b/src/server/api/endpoints/i/export-mute.ts @@ -13,6 +13,4 @@ export const meta = { export default define(meta, async (ps, user) => { createExportMuteJob(user); - - return; }); diff --git a/src/server/api/endpoints/i/export-notes.ts b/src/server/api/endpoints/i/export-notes.ts index 2881aa2697..dd32c18d11 100644 --- a/src/server/api/endpoints/i/export-notes.ts +++ b/src/server/api/endpoints/i/export-notes.ts @@ -13,6 +13,4 @@ export const meta = { export default define(meta, async (ps, user) => { createExportNotesJob(user); - - return; }); diff --git a/src/server/api/endpoints/i/export-user-lists.ts b/src/server/api/endpoints/i/export-user-lists.ts index 9d7424ad89..7650ca7210 100644 --- a/src/server/api/endpoints/i/export-user-lists.ts +++ b/src/server/api/endpoints/i/export-user-lists.ts @@ -13,6 +13,4 @@ export const meta = { export default define(meta, async (ps, user) => { createExportUserListsJob(user); - - return; }); diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts index 7ea6f7b966..d2d149b2d1 100644 --- a/src/server/api/endpoints/i/favorites.ts +++ b/src/server/api/endpoints/i/favorites.ts @@ -1,7 +1,8 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; -import Favorite, { packMany } from '../../../../models/favorite'; +import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; +import { NoteFavorites } from '../../../../models'; +import { makePaginationQuery } from '../../common/make-pagination-query'; export const meta = { desc: { @@ -23,42 +24,22 @@ export const meta = { sinceId: { validator: $.optional.type(ID), - transform: transform, }, untilId: { validator: $.optional.type(ID), - transform: transform, - } + }, } }; export default define(meta, async (ps, user) => { - const query = { - userId: user._id - } as any; - - const sort = { - _id: -1 - }; - - if (ps.sinceId) { - sort._id = 1; - query._id = { - $gt: ps.sinceId - }; - } else if (ps.untilId) { - query._id = { - $lt: ps.untilId - }; - } + const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) + .andWhere(`favorite.userId = :meId`, { meId: user.id }) + .leftJoinAndSelect('favorite.note', 'note'); - // Get favorites - const favorites = await Favorite - .find(query, { - limit: ps.limit, - sort: sort - }); + const favorites = await query + .take(ps.limit) + .getMany(); - return await packMany(favorites, user); + return await NoteFavorites.packMany(favorites, user); }); diff --git a/src/server/api/endpoints/i/import-following.ts b/src/server/api/endpoints/i/import-following.ts index f188291bc2..deafec18ec 100644 --- a/src/server/api/endpoints/i/import-following.ts +++ b/src/server/api/endpoints/i/import-following.ts @@ -1,10 +1,10 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; +import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { createImportFollowingJob } from '../../../../queue'; import ms = require('ms'); -import DriveFile from '../../../../models/drive-file'; import { ApiError } from '../../error'; +import { DriveFiles } from '../../../../models'; export const meta = { secure: true, @@ -17,7 +17,6 @@ export const meta = { params: { fileId: { validator: $.type(ID), - transform: transform, } }, @@ -49,16 +48,12 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const file = await DriveFile.findOne({ - _id: ps.fileId - }); + const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); - //if (!file.contentType.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); - if (file.length > 50000) throw new ApiError(meta.errors.tooBigFile); - if (file.length === 0) throw new ApiError(meta.errors.emptyFile); + //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); + if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportFollowingJob(user, file._id); - - return; + createImportFollowingJob(user, file.id); }); diff --git a/src/server/api/endpoints/i/import-user-lists.ts b/src/server/api/endpoints/i/import-user-lists.ts index ed3085e5f8..b7d9d029b7 100644 --- a/src/server/api/endpoints/i/import-user-lists.ts +++ b/src/server/api/endpoints/i/import-user-lists.ts @@ -1,10 +1,10 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; +import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import { createImportUserListsJob } from '../../../../queue'; import ms = require('ms'); -import DriveFile from '../../../../models/drive-file'; import { ApiError } from '../../error'; +import { DriveFiles } from '../../../../models'; export const meta = { secure: true, @@ -17,7 +17,6 @@ export const meta = { params: { fileId: { validator: $.type(ID), - transform: transform, } }, @@ -49,16 +48,12 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const file = await DriveFile.findOne({ - _id: ps.fileId - }); + const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); - //if (!file.contentType.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); - if (file.length > 30000) throw new ApiError(meta.errors.tooBigFile); - if (file.length === 0) throw new ApiError(meta.errors.emptyFile); + //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); + if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportUserListsJob(user, file._id); - - return; + createImportUserListsJob(user, file.id); }); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index d3e3064abd..9b016e0a2d 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -1,11 +1,9 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; -import Notification from '../../../../models/notification'; -import { packMany } from '../../../../models/notification'; -import { getFriendIds } from '../../common/get-friends'; -import read from '../../common/read-notification'; +import { ID } from '../../../../misc/cafy-id'; +import { readNotification } from '../../common/read-notification'; import define from '../../define'; -import { getHideUserIds } from '../../common/get-hide-users'; +import { makePaginationQuery } from '../../common/make-pagination-query'; +import { Notifications, Followings, Mutings } from '../../../../models'; export const meta = { desc: { @@ -17,7 +15,7 @@ export const meta = { requireCredential: true, - kind: 'account-read', + kind: 'read:notifications', params: { limit: { @@ -27,12 +25,10 @@ export const meta = { sinceId: { validator: $.optional.type(ID), - transform: transform, }, untilId: { validator: $.optional.type(ID), - transform: transform, }, following: { @@ -46,12 +42,12 @@ export const meta = { }, includeTypes: { - validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])), + validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'])), default: [] as string[] }, excludeTypes: { - validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])), + validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'])), default: [] as string[] } }, @@ -65,63 +61,38 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const hideUserIds = await getHideUserIds(user); + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: user.id }); - const query = { - notifieeId: user._id, - $and: [{ - notifierId: { - $nin: hideUserIds - } - }] - } as any; + const mutingQuery = Mutings.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: user.id }); - const sort = { - _id: -1 - }; + const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) + .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) + .leftJoinAndSelect('notification.notifier', 'notifier'); - if (ps.following) { - // ID list of the user itself and other users who the user follows - const followingIds = await getFriendIds(user._id); - - query.$and.push({ - notifierId: { - $in: followingIds - } - }); - } + query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`); + query.setParameters(mutingQuery.getParameters()); - if (ps.sinceId) { - sort._id = 1; - query._id = { - $gt: ps.sinceId - }; - } else if (ps.untilId) { - query._id = { - $lt: ps.untilId - }; + if (ps.following) { + query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: user.id }); + query.setParameters(followingQuery.getParameters()); } if (ps.includeTypes.length > 0) { - query.type = { - $in: ps.includeTypes - }; + query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes }); } else if (ps.excludeTypes.length > 0) { - query.type = { - $nin: ps.excludeTypes - }; + query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); } - const notifications = await Notification - .find(query, { - limit: ps.limit, - sort: sort - }); + const notifications = await query.take(ps.limit).getMany(); // Mark all as read if (notifications.length > 0 && ps.markAsRead) { - read(user._id, notifications); + readNotification(user.id, notifications.map(x => x.id)); } - return await packMany(notifications); + return await Notifications.packMany(notifications); }); diff --git a/src/server/api/endpoints/i/pin.ts b/src/server/api/endpoints/i/pin.ts index 8d853d45c8..ac104b19f9 100644 --- a/src/server/api/endpoints/i/pin.ts +++ b/src/server/api/endpoints/i/pin.ts @@ -1,9 +1,9 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; -import { pack } from '../../../../models/user'; +import { ID } from '../../../../misc/cafy-id'; import { addPinned } from '../../../../services/i/pin'; import define from '../../define'; import { ApiError } from '../../error'; +import { Users } from '../../../../models'; export const meta = { stability: 'stable', @@ -16,12 +16,11 @@ export const meta = { requireCredential: true, - kind: 'account-write', + kind: 'write:account', params: { noteId: { validator: $.type(ID), - transform: transform, desc: { 'ja-JP': '対象の投稿のID', 'en-US': 'Target note ID' @@ -58,7 +57,7 @@ export default define(meta, async (ps, user) => { throw e; }); - return await pack(user, user, { + return await Users.pack(user, user, { detail: true }); }); diff --git a/src/server/api/endpoints/i/read-all-messaging-messages.ts b/src/server/api/endpoints/i/read-all-messaging-messages.ts index bbbfa0d7b3..e8ada277e9 100644 --- a/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -1,7 +1,6 @@ -import User from '../../../../models/user'; import { publishMainStream } from '../../../../services/stream'; -import Message from '../../../../models/messaging-message'; import define from '../../define'; +import { MessagingMessages } from '../../../../models'; export const meta = { desc: { @@ -13,7 +12,7 @@ export const meta = { requireCredential: true, - kind: 'account-write', + kind: 'write:account', params: { } @@ -21,24 +20,12 @@ export const meta = { export default define(meta, async (ps, user) => { // Update documents - await Message.update({ - recipientId: user._id, + await MessagingMessages.update({ + recipientId: user.id, isRead: false }, { - $set: { - isRead: true - } - }, { - multi: true - }); - - User.update({ _id: user._id }, { - $set: { - hasUnreadMessagingMessage: false - } + isRead: true }); - publishMainStream(user._id, 'readAllMessagingMessages'); - - return; + publishMainStream(user.id, 'readAllMessagingMessages'); }); diff --git a/src/server/api/endpoints/i/read-all-unread-notes.ts b/src/server/api/endpoints/i/read-all-unread-notes.ts index 742c2d9908..cc8ebf58ec 100644 --- a/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,7 +1,6 @@ -import User from '../../../../models/user'; import { publishMainStream } from '../../../../services/stream'; -import NoteUnread from '../../../../models/note-unread'; import define from '../../define'; +import { NoteUnreads } from '../../../../models'; export const meta = { desc: { @@ -13,7 +12,7 @@ export const meta = { requireCredential: true, - kind: 'account-write', + kind: 'write:account', params: { } @@ -21,20 +20,11 @@ export const meta = { export default define(meta, async (ps, user) => { // Remove documents - await NoteUnread.remove({ - userId: user._id - }); - - User.update({ _id: user._id }, { - $set: { - hasUnreadMentions: false, - hasUnreadSpecifiedNotes: false - } + await NoteUnreads.delete({ + userId: user.id }); // 全て既読になったイベントを発行 - publishMainStream(user._id, 'readAllUnreadMentions'); - publishMainStream(user._id, 'readAllUnreadSpecifiedNotes'); - - return; + publishMainStream(user.id, 'readAllUnreadMentions'); + publishMainStream(user.id, 'readAllUnreadSpecifiedNotes'); }); diff --git a/src/server/api/endpoints/i/regenerate-token.ts b/src/server/api/endpoints/i/regenerate-token.ts index ad10b99b36..729c1a300a 100644 --- a/src/server/api/endpoints/i/regenerate-token.ts +++ b/src/server/api/endpoints/i/regenerate-token.ts @@ -1,9 +1,9 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; -import User from '../../../../models/user'; import { publishMainStream } from '../../../../services/stream'; import generateUserToken from '../../common/generate-native-user-token'; import define from '../../define'; +import { Users } from '../../../../models'; export const meta = { requireCredential: true, @@ -28,14 +28,10 @@ export default define(meta, async (ps, user) => { // Generate secret const secret = generateUserToken(); - await User.update(user._id, { - $set: { - 'token': secret - } + await Users.update(user.id, { + token: secret }); // Publish event - publishMainStream(user._id, 'myTokenRegenerated'); - - return; + publishMainStream(user.id, 'myTokenRegenerated'); }); diff --git a/src/server/api/endpoints/i/signin-history.ts b/src/server/api/endpoints/i/signin-history.ts index 87160a9f91..e9ae19d734 100644 --- a/src/server/api/endpoints/i/signin-history.ts +++ b/src/server/api/endpoints/i/signin-history.ts @@ -1,7 +1,8 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; -import Signin, { pack } from '../../../../models/signin'; +import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; +import { Signins } from '../../../../models'; +import { makePaginationQuery } from '../../common/make-pagination-query'; export const meta = { requireCredential: true, @@ -16,41 +17,19 @@ export const meta = { sinceId: { validator: $.optional.type(ID), - transform: transform, }, untilId: { validator: $.optional.type(ID), - transform: transform, } } }; export default define(meta, async (ps, user) => { - const query = { - userId: user._id - } as any; - - const sort = { - _id: -1 - }; - - if (ps.sinceId) { - sort._id = 1; - query._id = { - $gt: ps.sinceId - }; - } else if (ps.untilId) { - query._id = { - $lt: ps.untilId - }; - } + const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) + .andWhere(`signin.userId = :meId`, { meId: user.id }); - const history = await Signin - .find(query, { - limit: ps.limit, - sort: sort - }); + const history = await query.take(ps.limit).getMany(); - return await Promise.all(history.map(record => pack(record))); + return await Promise.all(history.map(record => Signins.pack(record))); }); diff --git a/src/server/api/endpoints/i/unpin.ts b/src/server/api/endpoints/i/unpin.ts index 184d46f2c3..4688533578 100644 --- a/src/server/api/endpoints/i/unpin.ts +++ b/src/server/api/endpoints/i/unpin.ts @@ -1,9 +1,9 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; -import { pack } from '../../../../models/user'; +import { ID } from '../../../../misc/cafy-id'; import { removePinned } from '../../../../services/i/pin'; import define from '../../define'; import { ApiError } from '../../error'; +import { Users } from '../../../../models'; export const meta = { stability: 'stable', @@ -16,12 +16,11 @@ export const meta = { requireCredential: true, - kind: 'account-write', + kind: 'write:account', params: { noteId: { validator: $.type(ID), - transform: transform, desc: { 'ja-JP': '対象の投稿のID', 'en-US': 'Target note ID' @@ -44,7 +43,7 @@ export default define(meta, async (ps, user) => { throw e; }); - return await pack(user, user, { + return await Users.pack(user, user, { detail: true }); }); diff --git a/src/server/api/endpoints/i/update-client-setting.ts b/src/server/api/endpoints/i/update-client-setting.ts index 79cd04e169..edbfe28f35 100644 --- a/src/server/api/endpoints/i/update-client-setting.ts +++ b/src/server/api/endpoints/i/update-client-setting.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; -import User from '../../../../models/user'; import { publishMainStream } from '../../../../services/stream'; import define from '../../define'; +import { Users } from '../../../../models'; export const meta = { requireCredential: true, @@ -10,7 +10,7 @@ export const meta = { params: { name: { - validator: $.str + validator: $.str.match(/^[a-zA-Z]+$/) }, value: { @@ -20,18 +20,18 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const x: any = {}; - x[`clientSettings.${ps.name}`] = ps.value; - - await User.update(user._id, { - $set: x - }); + await Users.createQueryBuilder().update() + .set({ + clientData: { + [ps.name]: ps.value + }, + }) + .where('id = :id', { id: user.id }) + .execute(); // Publish event - publishMainStream(user._id, 'clientSettingUpdated', { + publishMainStream(user.id, 'clientSettingUpdated', { key: ps.name, value: ps.value }); - - return; }); diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index c90462d850..253017535f 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -1,5 +1,4 @@ import $ from 'cafy'; -import User, { pack } from '../../../../models/user'; import { publishMainStream } from '../../../../services/stream'; import define from '../../define'; import * as nodemailer from 'nodemailer'; @@ -9,6 +8,7 @@ import config from '../../../../config'; import * as ms from 'ms'; import * as bcrypt from 'bcryptjs'; import { apiLogger } from '../../logger'; +import { Users } from '../../../../models'; export const meta = { requireCredential: true, @@ -39,29 +39,25 @@ export default define(meta, async (ps, user) => { throw new Error('incorrect password'); } - await User.update(user._id, { - $set: { - email: ps.email, - emailVerified: false, - emailVerifyCode: null - } + await Users.update(user.id, { + email: ps.email, + emailVerified: false, + emailVerifyCode: null }); - const iObj = await pack(user._id, user, { + const iObj = await Users.pack(user.id, user, { detail: true, includeSecrets: true }); // Publish meUpdated event - publishMainStream(user._id, 'meUpdated', iObj); + publishMainStream(user.id, 'meUpdated', iObj); if (ps.email != null) { const code = rndstr('a-z0-9', 16); - await User.update(user._id, { - $set: { - emailVerifyCode: code - } + await Users.update(user.id, { + emailVerifyCode: code }); const meta = await fetchMeta(); @@ -84,7 +80,7 @@ export default define(meta, async (ps, user) => { transporter.sendMail({ from: meta.email, to: ps.email, - subject: meta.name, + subject: meta.name || 'Misskey', text: `To verify email, please click this link: ${link}` }, (error, info) => { if (error) { diff --git a/src/server/api/endpoints/i/update-home.ts b/src/server/api/endpoints/i/update-home.ts deleted file mode 100644 index e2c319887f..0000000000 --- a/src/server/api/endpoints/i/update-home.ts +++ /dev/null @@ -1,33 +0,0 @@ -import $ from 'cafy'; -import User from '../../../../models/user'; -import { publishMainStream } from '../../../../services/stream'; -import define from '../../define'; - -export const meta = { - requireCredential: true, - - secure: true, - - params: { - home: { - validator: $.arr($.obj({ - name: $.str, - id: $.str, - place: $.str, - data: $.obj() - }).strict()) - } - } -}; - -export default define(meta, async (ps, user) => { - await User.update(user._id, { - $set: { - 'clientSettings.home': ps.home - } - }); - - publishMainStream(user._id, 'homeUpdated', ps.home); - - return; -}); diff --git a/src/server/api/endpoints/i/update-mobile-home.ts b/src/server/api/endpoints/i/update-mobile-home.ts deleted file mode 100644 index 642e2b3e09..0000000000 --- a/src/server/api/endpoints/i/update-mobile-home.ts +++ /dev/null @@ -1,32 +0,0 @@ -import $ from 'cafy'; -import User from '../../../../models/user'; -import { publishMainStream } from '../../../../services/stream'; -import define from '../../define'; - -export const meta = { - requireCredential: true, - - secure: true, - - params: { - home: { - validator: $.arr($.obj({ - name: $.str, - id: $.str, - data: $.obj() - }).strict()) - } - } -}; - -export default define(meta, async (ps, user) => { - await User.update(user._id, { - $set: { - 'clientSettings.mobileHome': ps.home - } - }); - - publishMainStream(user._id, 'mobileHomeUpdated', ps.home); - - return; -}); diff --git a/src/server/api/endpoints/i/update-widget.ts b/src/server/api/endpoints/i/update-widget.ts deleted file mode 100644 index 67d342278d..0000000000 --- a/src/server/api/endpoints/i/update-widget.ts +++ /dev/null @@ -1,88 +0,0 @@ -import $ from 'cafy'; -import User from '../../../../models/user'; -import { publishMainStream } from '../../../../services/stream'; -import define from '../../define'; - -export const meta = { - requireCredential: true, - - secure: true, - - params: { - id: { - validator: $.str - }, - - data: { - validator: $.obj() - } - } -}; - -export default define(meta, async (ps, user) => { - if (ps.id == null && ps.data == null) throw new Error('you need to set id and data params if home param unset'); - - let widget; - - //#region Desktop home - if (widget == null && user.clientSettings.home) { - const desktopHome = user.clientSettings.home; - widget = desktopHome.find((w: any) => w.id == ps.id); - if (widget) { - widget.data = ps.data; - - await User.update(user._id, { - $set: { - 'clientSettings.home': desktopHome - } - }); - } - } - //#endregion - - //#region Mobile home - if (widget == null && user.clientSettings.mobileHome) { - const mobileHome = user.clientSettings.mobileHome; - widget = mobileHome.find((w: any) => w.id == ps.id); - if (widget) { - widget.data = ps.data; - - await User.update(user._id, { - $set: { - 'clientSettings.mobileHome': mobileHome - } - }); - } - } - //#endregion - - //#region Deck - if (widget == null && user.clientSettings.deck && user.clientSettings.deck.columns) { - const deck = user.clientSettings.deck; - for (const c of deck.columns.filter((c: any) => c.type == 'widgets')) { - for (const w of c.widgets.filter((w: any) => w.id == ps.id)) { - widget = w; - } - } - if (widget) { - widget.data = ps.data; - - await User.update(user._id, { - $set: { - 'clientSettings.deck': deck - } - }); - } - } - //#endregion - - if (widget) { - publishMainStream(user._id, 'widgetUpdated', { - id: ps.id, data: ps.data - }); - - return; - } else { - throw new Error('widget not found'); - } -}); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 099ef33990..f3e5d41021 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -1,18 +1,16 @@ import $ from 'cafy'; -import ID, { transform } from '../../../../misc/cafy-id'; -import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack } from '../../../../models/user'; +import { ID } from '../../../../misc/cafy-id'; import { publishMainStream } from '../../../../services/stream'; -import DriveFile from '../../../../models/drive-file'; import acceptAllFollowRequests from '../../../../services/following/requests/accept-all'; import { publishToFollowers } from '../../../../services/i/update'; import define from '../../define'; -import getDriveFileUrl from '../../../../misc/get-drive-file-url'; import { parse, parsePlain } from '../../../../mfm/parse'; import extractEmojis from '../../../../misc/extract-emojis'; import extractHashtags from '../../../../misc/extract-hashtags'; import * as langmap from 'langmap'; import { updateHashtag } from '../../../../services/update-hashtag'; import { ApiError } from '../../error'; +import { Users, DriveFiles } from '../../../../models'; export const meta = { desc: { @@ -24,18 +22,18 @@ export const meta = { requireCredential: true, - kind: 'account-write', + kind: 'write:account', params: { name: { - validator: $.optional.nullable.str.pipe(isValidName), + validator: $.optional.nullable.str.pipe(Users.isValidName), desc: { 'ja-JP': '名前(ハンドルネームやニックネーム)' } }, description: { - validator: $.optional.nullable.str.pipe(isValidDescription), + validator: $.optional.nullable.str.pipe(Users.isValidDescription), desc: { 'ja-JP': 'アカウントの説明や自己紹介' } @@ -49,14 +47,14 @@ export const meta = { }, location: { - validator: $.optional.nullable.str.pipe(isValidLocation), + validator: $.optional.nullable.str.pipe(Users.isValidLocation), desc: { 'ja-JP': '住んでいる地域、所在' } }, birthday: { - validator: $.optional.nullable.str.pipe(isValidBirthday), + validator: $.optional.nullable.str.pipe(Users.isValidBirthday), desc: { 'ja-JP': '誕生日 (YYYY-MM-DD形式)' } @@ -64,7 +62,6 @@ export const meta = { avatarId: { validator: $.optional.nullable.type(ID), - transform: transform, desc: { 'ja-JP': 'アイコンに設定する画像のドライブファイルID' } @@ -72,20 +69,11 @@ export const meta = { bannerId: { validator: $.optional.nullable.type(ID), - transform: transform, desc: { 'ja-JP': 'バナーに設定する画像のドライブファイルID' } }, - wallpaperId: { - validator: $.optional.nullable.type(ID), - transform: transform, - desc: { - 'ja-JP': '壁紙に設定する画像のドライブファイルID' - } - }, - isLocked: { validator: $.optional.bool, desc: { @@ -171,116 +159,76 @@ export default define(meta, async (ps, user, app) => { if (ps.name !== undefined) updates.name = ps.name; if (ps.description !== undefined) updates.description = ps.description; if (ps.lang !== undefined) updates.lang = ps.lang; - if (ps.location !== undefined) updates['profile.location'] = ps.location; - if (ps.birthday !== undefined) updates['profile.birthday'] = ps.birthday; + if (ps.location !== undefined) updates.location = ps.location; + if (ps.birthday !== undefined) updates.birthday = ps.birthday; if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; - if (ps.wallpaperId !== undefined) updates.wallpaperId = ps.wallpaperId; if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked; if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot; if (typeof ps.carefulBot == 'boolean') updates.carefulBot = ps.carefulBot; if (typeof ps.autoAcceptFollowed == 'boolean') updates.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat; - if (typeof ps.autoWatch == 'boolean') updates['settings.autoWatch'] = ps.autoWatch; - if (typeof ps.alwaysMarkNsfw == 'boolean') updates['settings.alwaysMarkNsfw'] = ps.alwaysMarkNsfw; + if (typeof ps.autoWatch == 'boolean') updates.autoWatch = ps.autoWatch; + if (typeof ps.alwaysMarkNsfw == 'boolean') updates.alwaysMarkNsfw = ps.alwaysMarkNsfw; if (ps.avatarId) { - const avatar = await DriveFile.findOne({ - _id: ps.avatarId - }); + const avatar = await DriveFiles.findOne(ps.avatarId); - if (avatar == null) throw new ApiError(meta.errors.noSuchAvatar); - if (!avatar.contentType.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); + if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); + if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); - if (avatar.metadata.deletedAt) { - updates.avatarUrl = null; - } else { - updates.avatarUrl = getDriveFileUrl(avatar, true); + updates.avatarUrl = avatar.thumbnailUrl; - if (avatar.metadata.properties.avgColor) { - updates.avatarColor = avatar.metadata.properties.avgColor; - } + if (avatar.properties.avgColor) { + updates.avatarColor = avatar.properties.avgColor; } } if (ps.bannerId) { - const banner = await DriveFile.findOne({ - _id: ps.bannerId - }); + const banner = await DriveFiles.findOne(ps.bannerId); - if (banner == null) throw new ApiError(meta.errors.noSuchBanner); - if (!banner.contentType.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); + if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); + if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); - if (banner.metadata.deletedAt) { - updates.bannerUrl = null; - } else { - updates.bannerUrl = getDriveFileUrl(banner, false); + updates.bannerUrl = banner.webpublicUrl; - if (banner.metadata.properties.avgColor) { - updates.bannerColor = banner.metadata.properties.avgColor; - } - } - } - - if (ps.wallpaperId !== undefined) { - if (ps.wallpaperId === null) { - updates.wallpaperUrl = null; - updates.wallpaperColor = null; - } else { - const wallpaper = await DriveFile.findOne({ - _id: ps.wallpaperId - }); - - if (wallpaper == null) throw new Error('wallpaper not found'); - - if (wallpaper.metadata.deletedAt) { - updates.wallpaperUrl = null; - } else { - updates.wallpaperUrl = getDriveFileUrl(wallpaper); - - if (wallpaper.metadata.properties.avgColor) { - updates.wallpaperColor = wallpaper.metadata.properties.avgColor; - } - } + if (banner.properties.avgColor) { + updates.bannerColor = banner.properties.avgColor; } } //#region emojis/tags - if (updates.name != null || updates.description != null) { - let emojis = [] as string[]; - let tags = [] as string[]; + let emojis = [] as string[]; + let tags = [] as string[]; - if (updates.name != null) { - const tokens = parsePlain(updates.name); - emojis = emojis.concat(extractEmojis(tokens)); - } + if (updates.name != null) { + const tokens = parsePlain(updates.name); + emojis = emojis.concat(extractEmojis(tokens)); + } - if (updates.description != null) { - const tokens = parse(updates.description); - emojis = emojis.concat(extractEmojis(tokens)); - tags = extractHashtags(tokens).map(tag => tag.toLowerCase()); - } + if (updates.description != null) { + const tokens = parse(updates.description); + emojis = emojis.concat(extractEmojis(tokens)); + tags = extractHashtags(tokens).map(tag => tag.toLowerCase()); + } - updates.emojis = emojis; - updates.tags = tags; + updates.emojis = emojis; + updates.tags = tags; - // ハッシュタグ更新 - for (const tag of tags) updateHashtag(user, tag, true, true); - for (const tag of (user.tags || []).filter(x => !tags.includes(x))) updateHashtag(user, tag, true, false); - } + // ハッシュタグ更新 + for (const tag of tags) updateHashtag(user, tag, true, true); + for (const tag of user.tags.filter(x => !tags.includes(x))) updateHashtag(user, tag, true, false); //#endregion - await User.update(user._id, { - $set: updates - }); + await Users.update(user.id, updates); - const iObj = await pack(user._id, user, { + const iObj = await Users.pack(user.id, user, { detail: true, includeSecrets: isSecure }); // Publish meUpdated event - publishMainStream(user._id, 'meUpdated', iObj); + publishMainStream(user.id, 'meUpdated', iObj); // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { @@ -288,7 +236,7 @@ export default define(meta, async (ps, user, app) => { } // フォロワーにUpdateを配信 - publishToFollowers(user._id); + publishToFollowers(user.id); return iObj; }); -- cgit v1.2.3-freya From 4d64fd665e9adc88266b963747180e28712455b5 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 9 Apr 2019 23:40:55 +0900 Subject: Update update.ts --- src/server/api/endpoints/i/update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/server/api/endpoints/i') diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index f3e5d41021..54e7f33bdb 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -177,7 +177,7 @@ export default define(meta, async (ps, user, app) => { if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); - updates.avatarUrl = avatar.thumbnailUrl; + updates.avatarUrl = DriveFiles.getPublicUrl(avatar, true); if (avatar.properties.avgColor) { updates.avatarColor = avatar.properties.avgColor; @@ -190,7 +190,7 @@ export default define(meta, async (ps, user, app) => { if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); - updates.bannerUrl = banner.webpublicUrl; + updates.bannerUrl = DriveFiles.getPublicUrl(banner, false); if (banner.properties.avgColor) { updates.bannerColor = banner.properties.avgColor; -- cgit v1.2.3-freya From 626cfb61ac3940bee7a3acf1b1c5c4cae4ae410c Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 10 Apr 2019 15:04:27 +0900 Subject: テーブル分割 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db/postgre.ts | 4 +- src/migrate.ts | 20 ++- src/models/entities/user-keypair.ts | 5 +- src/models/entities/user-profile.ts | 196 +++++++++++++++++++++ src/models/entities/user-publickey.ts | 5 +- src/models/entities/user-service-linking.ts | 108 ------------ src/models/entities/user.ts | 81 --------- src/models/index.ts | 4 +- src/models/repositories/user.ts | 15 +- src/remote/activitypub/models/person.ts | 23 ++- src/remote/activitypub/renderer/person.ts | 26 +-- src/server/api/endpoints/admin/reset-password.ts | 6 +- src/server/api/endpoints/i/2fa/done.ts | 16 +- src/server/api/endpoints/i/2fa/register.ts | 8 +- src/server/api/endpoints/i/2fa/unregister.ts | 10 +- src/server/api/endpoints/i/change-password.ts | 8 +- src/server/api/endpoints/i/delete-account.ts | 6 +- src/server/api/endpoints/i/regenerate-token.ts | 6 +- .../api/endpoints/i/update-client-setting.ts | 4 +- src/server/api/endpoints/i/update-email.ts | 10 +- src/server/api/endpoints/i/update.ts | 28 +-- src/server/api/endpoints/notes/polls/vote.ts | 6 +- src/server/api/private/signin.ts | 10 +- src/server/api/private/signup.ts | 18 +- src/server/api/service/discord.ts | 14 +- src/server/api/service/github.ts | 8 +- src/server/api/service/twitter.ts | 8 +- src/server/index.ts | 8 +- src/server/web/feed.ts | 6 +- src/services/drive/add-file.ts | 6 +- src/services/following/create.ts | 8 +- src/services/note/create.ts | 8 +- src/services/note/polls/vote.ts | 6 +- src/services/note/reaction/create.ts | 6 +- 34 files changed, 368 insertions(+), 333 deletions(-) create mode 100644 src/models/entities/user-profile.ts delete mode 100644 src/models/entities/user-service-linking.ts (limited to 'src/server/api/endpoints/i') diff --git a/src/db/postgre.ts b/src/db/postgre.ts index bc5ee4ce8c..e5726e9c87 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -36,10 +36,10 @@ import { Emoji } from '../models/entities/emoji'; import { ReversiGame } from '../models/entities/games/reversi/game'; import { ReversiMatching } from '../models/entities/games/reversi/matching'; import { UserNotePining } from '../models/entities/user-note-pinings'; -import { UserServiceLinking } from '../models/entities/user-service-linking'; import { Poll } from '../models/entities/poll'; import { UserKeypair } from '../models/entities/user-keypair'; import { UserPublickey } from '../models/entities/user-publickey'; +import { UserProfile } from '../models/entities/user-profile'; const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); @@ -101,12 +101,12 @@ export function initDb(justBorrow = false, sync = false, log = false) { AuthSession, AccessToken, User, + UserProfile, UserKeypair, UserPublickey, UserList, UserListJoining, UserNotePining, - UserServiceLinking, Following, FollowRequest, Muting, diff --git a/src/migrate.ts b/src/migrate.ts index b9d6eb3396..f833ec180b 100644 --- a/src/migrate.ts +++ b/src/migrate.ts @@ -26,6 +26,7 @@ import { UserKeypair } from './models/entities/user-keypair'; import { extractPublic } from './crypto_key'; import { Emoji } from './models/entities/emoji'; import { toPuny } from './misc/convert-host'; +import { UserProfile } from './models/entities/user-profile'; const u = (config as any).mongodb.user ? encodeURIComponent((config as any).mongodb.user) : null; const p = (config as any).mongodb.pass ? encodeURIComponent((config as any).mongodb.pass) : null; @@ -70,6 +71,7 @@ const getDriveFileBucket = async (): Promise => { async function main() { await initDb(); const Users = getRepository(User); + const UserProfiles = getRepository(UserProfile); const DriveFiles = getRepository(DriveFile); const DriveFolders = getRepository(DriveFolder); const Notes = getRepository(Note); @@ -90,17 +92,11 @@ async function main() { usernameLower: user.username.toLowerCase(), host: toPuny(user.host), token: generateUserToken(), - password: user.password, isAdmin: user.isAdmin, - autoAcceptFollowed: true, - autoWatch: false, name: user.name, - location: user.profile ? user.profile.location : null, - birthday: user.profile ? user.profile.birthday : null, followersCount: user.followersCount, followingCount: user.followingCount, notesCount: user.notesCount, - description: user.description, isBot: user.isBot, isCat: user.isCat, isVerified: user.isVerified, @@ -108,9 +104,18 @@ async function main() { sharedInbox: user.sharedInbox, uri: user.uri, }); + await UserProfiles.save({ + userId: user._id.toHexString(), + description: user.description, + userHost: toPuny(user.host), + autoAcceptFollowed: true, + autoWatch: false, + password: user.password, + location: user.profile ? user.profile.location : null, + birthday: user.profile ? user.profile.birthday : null, + }); if (user.publicKey) { await UserPublickeys.save({ - id: genId(), userId: user._id.toHexString(), keyId: user.publicKey.id, keyPem: user.publicKey.publicKeyPem @@ -118,7 +123,6 @@ async function main() { } if (user.keypair) { await UserKeypairs.save({ - id: genId(), userId: user._id.toHexString(), publicKey: extractPublic(user.keypair), privateKey: user.keypair, diff --git a/src/models/entities/user-keypair.ts b/src/models/entities/user-keypair.ts index be264641f7..9181abf8cb 100644 --- a/src/models/entities/user-keypair.ts +++ b/src/models/entities/user-keypair.ts @@ -4,11 +4,8 @@ import { id } from '../id'; @Entity() export class UserKeypair { - @PrimaryColumn(id()) - public id: string; - @Index({ unique: true }) - @Column(id()) + @PrimaryColumn(id()) public userId: User['id']; @OneToOne(type => User, { diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts new file mode 100644 index 0000000000..24b92231f5 --- /dev/null +++ b/src/models/entities/user-profile.ts @@ -0,0 +1,196 @@ +import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; +import { id } from '../id'; +import { User } from './user'; + +@Entity() +export class UserProfile { + @Index({ unique: true }) + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The location of the User.' + }) + public location: string | null; + + @Column('char', { + length: 10, nullable: true, + comment: 'The birthday (YYYY-MM-DD) of the User.' + }) + public birthday: string | null; + + @Column('varchar', { + length: 1024, nullable: true, + comment: 'The description (bio) of the User.' + }) + public description: string | null; + + @Column('jsonb', { + default: [], + }) + public fields: { + name: string; + value: string; + }[]; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The email address of the User.' + }) + public email: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public emailVerifyCode: string | null; + + @Column('boolean', { + default: false, + }) + public emailVerified: boolean; + + @Column('varchar', { + length: 128, nullable: true, + }) + public twoFactorTempSecret: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public twoFactorSecret: string | null; + + @Column('boolean', { + default: false, + }) + public twoFactorEnabled: boolean; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The password hash of the User. It will be null if the origin of the user is local.' + }) + public password: string | null; + + @Column('jsonb', { + default: {}, + comment: 'The client-specific data of the User.' + }) + public clientData: Record; + + @Column('boolean', { + default: false, + }) + public autoWatch: boolean; + + @Column('boolean', { + default: false, + }) + public autoAcceptFollowed: boolean; + + @Column('boolean', { + default: false, + }) + public alwaysMarkNsfw: boolean; + + @Column('boolean', { + default: false, + }) + public carefulBot: boolean; + + //#region Linking + @Column('boolean', { + default: false, + }) + public twitter: boolean; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterAccessToken: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterAccessTokenSecret: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterUserId: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterScreenName: string | null; + + @Column('boolean', { + default: false, + }) + public github: boolean; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public githubAccessToken: string | null; + + @Column('integer', { + nullable: true, default: null, + }) + public githubId: number | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public githubLogin: string | null; + + @Column('boolean', { + default: false, + }) + public discord: boolean; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordAccessToken: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordRefreshToken: string | null; + + @Column('integer', { + nullable: true, default: null, + }) + public discordExpiresDate: number | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordId: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordUsername: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordDiscriminator: string | null; + //#endregion + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public userHost: string | null; + //#endregion +} diff --git a/src/models/entities/user-publickey.ts b/src/models/entities/user-publickey.ts index 6c019f3313..81c42404fa 100644 --- a/src/models/entities/user-publickey.ts +++ b/src/models/entities/user-publickey.ts @@ -4,11 +4,8 @@ import { id } from '../id'; @Entity() export class UserPublickey { - @PrimaryColumn(id()) - public id: string; - @Index({ unique: true }) - @Column(id()) + @PrimaryColumn(id()) public userId: User['id']; @OneToOne(type => User, { diff --git a/src/models/entities/user-service-linking.ts b/src/models/entities/user-service-linking.ts deleted file mode 100644 index 3d99554e1e..0000000000 --- a/src/models/entities/user-service-linking.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { User } from './user'; -import { id } from '../id'; - -@Entity() -export class UserServiceLinking { - @PrimaryColumn(id()) - public id: string; - - @Index({ unique: true }) - @Column(id()) - public userId: User['id']; - - @OneToOne(type => User, { - onDelete: 'CASCADE' - }) - @JoinColumn() - public user: User | null; - - @Column('boolean', { - default: false, - }) - public twitter: boolean; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public twitterAccessToken: string | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public twitterAccessTokenSecret: string | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public twitterUserId: string | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public twitterScreenName: string | null; - - @Column('boolean', { - default: false, - }) - public github: boolean; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public githubAccessToken: string | null; - - @Column('integer', { - nullable: true, default: null, - }) - public githubId: number | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public githubLogin: string | null; - - @Column('boolean', { - default: false, - }) - public discord: boolean; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public discordAccessToken: string | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public discordRefreshToken: string | null; - - @Column('integer', { - nullable: true, default: null, - }) - public discordExpiresDate: number | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public discordId: string | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public discordUsername: string | null; - - @Column('varchar', { - length: 64, nullable: true, default: null, - }) - public discordDiscriminator: string | null; - - //#region Denormalized fields - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]' - }) - public userHost: string | null; - //#endregion -} diff --git a/src/models/entities/user.ts b/src/models/entities/user.ts index 0a2878c0c9..40d27a42bb 100644 --- a/src/models/entities/user.ts +++ b/src/models/entities/user.ts @@ -45,18 +45,6 @@ export class User { }) public name: string | null; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The location of the User.' - }) - public location: string | null; - - @Column('char', { - length: 10, nullable: true, - comment: 'The birthday (YYYY-MM-DD) of the User.' - }) - public birthday: string | null; - @Column('integer', { default: 0, comment: 'The count of followers.' @@ -101,44 +89,12 @@ export class User { @JoinColumn() public banner: DriveFile | null; - @Column('varchar', { - length: 1024, nullable: true, - comment: 'The description (bio) of the User.' - }) - public description: string | null; - @Index() @Column('varchar', { length: 128, array: true, default: '{}' }) public tags: string[]; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The email address of the User.' - }) - public email: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public emailVerifyCode: string | null; - - @Column('boolean', { - default: false, - }) - public emailVerified: boolean; - - @Column('varchar', { - length: 128, nullable: true, - }) - public twoFactorTempSecret: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public twoFactorSecret: string | null; - @Column('varchar', { length: 256, nullable: true, }) @@ -206,11 +162,6 @@ export class User { }) public isVerified: boolean; - @Column('boolean', { - default: false, - }) - public twoFactorEnabled: boolean; - @Column('varchar', { length: 128, array: true, default: '{}' }) @@ -248,44 +199,12 @@ export class User { }) public uri: string | null; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The password hash of the User. It will be null if the origin of the user is local.' - }) - public password: string | null; - @Index({ unique: true }) @Column('char', { length: 16, nullable: true, unique: true, comment: 'The native access token of the User. It will be null if the origin of the user is local.' }) public token: string | null; - - @Column('jsonb', { - default: {}, - comment: 'The client-specific data of the User.' - }) - public clientData: Record; - - @Column('boolean', { - default: false, - }) - public autoWatch: boolean; - - @Column('boolean', { - default: false, - }) - public autoAcceptFollowed: boolean; - - @Column('boolean', { - default: false, - }) - public alwaysMarkNsfw: boolean; - - @Column('boolean', { - default: false, - }) - public carefulBot: boolean; } export interface ILocalUser extends User { diff --git a/src/models/index.ts b/src/models/index.ts index f88bb8d636..d66e4e710a 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -25,7 +25,6 @@ import { FollowRequestRepository } from './repositories/follow-request'; import { MutingRepository } from './repositories/muting'; import { BlockingRepository } from './repositories/blocking'; import { NoteReactionRepository } from './repositories/note-reaction'; -import { UserServiceLinking } from './entities/user-service-linking'; import { NotificationRepository } from './repositories/notification'; import { NoteFavoriteRepository } from './repositories/note-favorite'; import { ReversiMatchingRepository } from './repositories/games/reversi/matching'; @@ -35,6 +34,7 @@ import { AppRepository } from './repositories/app'; import { FollowingRepository } from './repositories/following'; import { AbuseUserReportRepository } from './repositories/abuse-user-report'; import { AuthSessionRepository } from './repositories/auth-session'; +import { UserProfile } from './entities/user-profile'; export const Apps = getCustomRepository(AppRepository); export const Notes = getCustomRepository(NoteRepository); @@ -45,12 +45,12 @@ export const NoteUnreads = getRepository(NoteUnread); export const Polls = getRepository(Poll); export const PollVotes = getRepository(PollVote); export const Users = getCustomRepository(UserRepository); +export const UserProfiles = getRepository(UserProfile); export const UserKeypairs = getRepository(UserKeypair); export const UserPublickeys = getRepository(UserPublickey); export const UserLists = getCustomRepository(UserListRepository); export const UserListJoinings = getRepository(UserListJoining); export const UserNotePinings = getRepository(UserNotePining); -export const UserServiceLinkings = getRepository(UserServiceLinking); export const Followings = getCustomRepository(FollowingRepository); export const FollowRequests = getCustomRepository(FollowRequestRepository); export const Instances = getRepository(Instance); diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index 3939e3142a..9e11b2aa88 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -1,6 +1,6 @@ import { EntityRepository, Repository, In } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; -import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings } from '..'; +import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; import rap from '@prezzemolo/rap'; @EntityRepository(User) @@ -80,6 +80,7 @@ export class UserRepository extends Repository { const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : []; + const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }) : null; return await rap({ id: user.id, @@ -116,9 +117,9 @@ export class UserRepository extends Repository { } : {}), ...(opts.detail ? { - description: user.description, - location: user.location, - birthday: user.birthday, + description: profile.description, + location: profile.location, + birthday: profile.birthday, followersCount: user.followersCount, followingCount: user.followingCount, notesCount: user.notesCount, @@ -131,9 +132,9 @@ export class UserRepository extends Repository { ...(opts.detail && meId === user.id ? { avatarId: user.avatarId, bannerId: user.bannerId, - autoWatch: user.autoWatch, - alwaysMarkNsfw: user.alwaysMarkNsfw, - carefulBot: user.carefulBot, + autoWatch: profile.autoWatch, + alwaysMarkNsfw: profile.alwaysMarkNsfw, + carefulBot: profile.carefulBot, hasUnreadMessagingMessage: MessagingMessages.count({ where: { recipientId: user.id, diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 6129dad3d3..a6f7482bba 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -14,16 +14,16 @@ import { IIdentifier } from './identifier'; import { apLogger } from '../logger'; import { Note } from '../../../models/entities/note'; import { updateHashtag } from '../../../services/update-hashtag'; -import { Users, UserNotePinings, Instances, DriveFiles, Followings, UserServiceLinkings, UserPublickeys } from '../../../models'; +import { Users, UserNotePinings, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '../../../models'; import { User, IRemoteUser } from '../../../models/entities/user'; import { Emoji } from '../../../models/entities/emoji'; import { UserNotePining } from '../../../models/entities/user-note-pinings'; import { genId } from '../../../misc/gen-id'; -import { UserServiceLinking } from '../../../models/entities/user-service-linking'; import { instanceChart, usersChart } from '../../../services/chart'; import { UserPublickey } from '../../../models/entities/user-publickey'; import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error'; import { toPuny } from '../../../misc/convert-host'; +import { UserProfile } from '../../../models/entities/user-profile'; const logger = apLogger; /** @@ -126,7 +126,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise tag.toLowerCase()); @@ -141,7 +141,6 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise); + await UserPublickeys.save({ - id: genId(), userId: user.id, keyId: person.publicKey.id, keyPem: person.publicKey.publicKeyPem } as UserPublickey); - await UserServiceLinkings.save({ - id: genId(), - userId: user.id - } as UserServiceLinking); - // Register host registerOrFetchInstanceDoc(host).then(i => { Instances.increment({ id: i.id }, 'usersCount', 1); @@ -347,7 +344,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje keyPem: person.publicKey.publicKeyPem }); - await UserServiceLinkings.update({ userId: exist.id }, { + await UserProfiles.update({ userId: exist.id }, { twitterUserId: services.twitter.userId, twitterScreenName: services.twitter.screenName, githubId: services.github.id, diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index 4c6b518eb6..e561e47c68 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -8,15 +8,15 @@ import { getEmojis } from './note'; import renderEmoji from './emoji'; import { IIdentifier } from '../models/identifier'; import renderHashtag from './hashtag'; -import { DriveFiles, UserServiceLinkings, UserKeypairs } from '../../../models'; +import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; export async function renderPerson(user: ILocalUser) { const id = `${config.url}/users/${user.id}`; - const [avatar, banner, links] = await Promise.all([ + const [avatar, banner, profile] = await Promise.all([ DriveFiles.findOne(user.avatarId), DriveFiles.findOne(user.bannerId), - UserServiceLinkings.findOne({ userId: user.id }) + UserProfiles.findOne({ userId: user.id }) ]); const attachment: { @@ -27,41 +27,41 @@ export async function renderPerson(user: ILocalUser) { identifier?: IIdentifier }[] = []; - if (links.twitter) { + if (profile.twitter) { attachment.push({ type: 'PropertyValue', name: 'Twitter', - value: `@${links.twitterScreenName}`, + value: `@${profile.twitterScreenName}`, identifier: { type: 'PropertyValue', name: 'misskey:authentication:twitter', - value: `${links.twitterUserId}@${links.twitterScreenName}` + value: `${profile.twitterUserId}@${profile.twitterScreenName}` } }); } - if (links.github) { + if (profile.github) { attachment.push({ type: 'PropertyValue', name: 'GitHub', - value: `@${links.githubLogin}`, + value: `@${profile.githubLogin}`, identifier: { type: 'PropertyValue', name: 'misskey:authentication:github', - value: `${links.githubId}@${links.githubLogin}` + value: `${profile.githubId}@${profile.githubLogin}` } }); } - if (links.discord) { + if (profile.discord) { attachment.push({ type: 'PropertyValue', name: 'Discord', - value: `${links.discordUsername}#${links.discordDiscriminator}`, + value: `${profile.discordUsername}#${profile.discordDiscriminator}`, identifier: { type: 'PropertyValue', name: 'misskey:authentication:discord', - value: `${links.discordId}@${links.discordUsername}#${links.discordDiscriminator}` + value: `${profile.discordId}@${profile.discordUsername}#${profile.discordDiscriminator}` } }); } @@ -93,7 +93,7 @@ export async function renderPerson(user: ILocalUser) { url: `${config.url}/@${user.username}`, preferredUsername: user.username, name: user.name, - summary: toHtml(parse(user.description)), + summary: toHtml(parse(profile.description)), icon: user.avatarId && renderImage(avatar), image: user.bannerId && renderImage(banner), tag, diff --git a/src/server/api/endpoints/admin/reset-password.ts b/src/server/api/endpoints/admin/reset-password.ts index 07b8b6d938..42df668606 100644 --- a/src/server/api/endpoints/admin/reset-password.ts +++ b/src/server/api/endpoints/admin/reset-password.ts @@ -3,7 +3,7 @@ import { ID } from '../../../../misc/cafy-id'; import define from '../../define'; import * as bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; -import { Users } from '../../../../models'; +import { Users, UserProfiles } from '../../../../models'; export const meta = { desc: { @@ -42,7 +42,9 @@ export default define(meta, async (ps) => { // Generate hash of password const hash = bcrypt.hashSync(passwd); - await Users.update(user.id, { + await UserProfiles.update({ + userId: user.id + }, { password: hash }); diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts index 8ccb09b8b7..edc7cefd26 100644 --- a/src/server/api/endpoints/i/2fa/done.ts +++ b/src/server/api/endpoints/i/2fa/done.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as speakeasy from 'speakeasy'; import define from '../../../define'; -import { Users } from '../../../../../models'; +import { UserProfiles } from '../../../../../models'; export const meta = { requireCredential: true, @@ -16,24 +16,26 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const _token = ps.token.replace(/\s/g, ''); + const token = ps.token.replace(/\s/g, ''); - if (user.twoFactorTempSecret == null) { + const profile = await UserProfiles.findOne({ userId: user.id }); + + if (profile.twoFactorTempSecret == null) { throw new Error('二段階認証の設定が開始されていません'); } const verified = (speakeasy as any).totp.verify({ - secret: user.twoFactorTempSecret, + secret: profile.twoFactorTempSecret, encoding: 'base32', - token: _token + token: token }); if (!verified) { throw new Error('not verified'); } - await Users.update(user.id, { - twoFactorSecret: user.twoFactorTempSecret, + await UserProfiles.update({ userId: user.id }, { + twoFactorSecret: profile.twoFactorTempSecret, twoFactorEnabled: true }); }); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index 5efe77900a..db9a2fe944 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -4,7 +4,7 @@ import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; import config from '../../../../../config'; import define from '../../../define'; -import { Users } from '../../../../../models'; +import { UserProfiles } from '../../../../../models'; export const meta = { requireCredential: true, @@ -19,8 +19,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(ps.password, user.password); + const same = await bcrypt.compare(ps.password, profile.password); if (!same) { throw new Error('incorrect password'); @@ -31,7 +33,7 @@ export default define(meta, async (ps, user) => { length: 32 }); - await Users.update(user.id, { + await UserProfiles.update({ userId: user.id }, { twoFactorTempSecret: secret.base32 }); diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts index fb3ecd4043..fa25b74391 100644 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ b/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; -import { Users } from '../../../../../models'; +import { UserProfiles } from '../../../../../models'; export const meta = { requireCredential: true, @@ -16,17 +16,17 @@ export const meta = { }; export default define(meta, async (ps, user) => { + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(ps.password, user.password); + const same = await bcrypt.compare(ps.password, profile.password); if (!same) { throw new Error('incorrect password'); } - await Users.update(user.id, { + await UserProfiles.update({ userId: user.id }, { twoFactorSecret: null, twoFactorEnabled: false }); - - return; }); diff --git a/src/server/api/endpoints/i/change-password.ts b/src/server/api/endpoints/i/change-password.ts index f8f977200f..d0e0695e18 100644 --- a/src/server/api/endpoints/i/change-password.ts +++ b/src/server/api/endpoints/i/change-password.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; -import { Users } from '../../../../models'; +import { UserProfiles } from '../../../../models'; export const meta = { requireCredential: true, @@ -20,8 +20,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(ps.currentPassword, user.password); + const same = await bcrypt.compare(ps.currentPassword, profile.password); if (!same) { throw new Error('incorrect password'); @@ -31,7 +33,7 @@ export default define(meta, async (ps, user) => { const salt = await bcrypt.genSalt(8); const hash = await bcrypt.hash(ps.newPassword, salt); - await Users.update(user.id, { + await UserProfiles.update({ userId: user.id }, { password: hash }); }); diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts index 5aff74e0cc..7ef7aa5fac 100644 --- a/src/server/api/endpoints/i/delete-account.ts +++ b/src/server/api/endpoints/i/delete-account.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; -import { Users } from '../../../../models'; +import { Users, UserProfiles } from '../../../../models'; export const meta = { requireCredential: true, @@ -16,8 +16,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(ps.password, user.password); + const same = await bcrypt.compare(ps.password, profile.password); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/regenerate-token.ts b/src/server/api/endpoints/i/regenerate-token.ts index 729c1a300a..ec53bca979 100644 --- a/src/server/api/endpoints/i/regenerate-token.ts +++ b/src/server/api/endpoints/i/regenerate-token.ts @@ -3,7 +3,7 @@ import * as bcrypt from 'bcryptjs'; import { publishMainStream } from '../../../../services/stream'; import generateUserToken from '../../common/generate-native-user-token'; import define from '../../define'; -import { Users } from '../../../../models'; +import { Users, UserProfiles } from '../../../../models'; export const meta = { requireCredential: true, @@ -18,8 +18,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(ps.password, user.password); + const same = await bcrypt.compare(ps.password, profile.password); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/update-client-setting.ts b/src/server/api/endpoints/i/update-client-setting.ts index edbfe28f35..49bcb35ae6 100644 --- a/src/server/api/endpoints/i/update-client-setting.ts +++ b/src/server/api/endpoints/i/update-client-setting.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import { publishMainStream } from '../../../../services/stream'; import define from '../../define'; -import { Users } from '../../../../models'; +import { UserProfiles } from '../../../../models'; export const meta = { requireCredential: true, @@ -20,7 +20,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - await Users.createQueryBuilder().update() + await UserProfiles.createQueryBuilder().update() .set({ clientData: { [ps.name]: ps.value diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index 253017535f..d98f0d753e 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -8,7 +8,7 @@ import config from '../../../../config'; import * as ms from 'ms'; import * as bcrypt from 'bcryptjs'; import { apiLogger } from '../../logger'; -import { Users } from '../../../../models'; +import { Users, UserProfiles } from '../../../../models'; export const meta = { requireCredential: true, @@ -32,14 +32,16 @@ export const meta = { }; export default define(meta, async (ps, user) => { + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(ps.password, user.password); + const same = await bcrypt.compare(ps.password, profile.password); if (!same) { throw new Error('incorrect password'); } - await Users.update(user.id, { + await UserProfiles.update({ userId: user.id }, { email: ps.email, emailVerified: false, emailVerifyCode: null @@ -56,7 +58,7 @@ export default define(meta, async (ps, user) => { if (ps.email != null) { const code = rndstr('a-z0-9', 16); - await Users.update(user.id, { + await UserProfiles.update({ userId: user.id }, { emailVerifyCode: code }); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 54e7f33bdb..ffc90b2f51 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -10,7 +10,9 @@ import extractHashtags from '../../../../misc/extract-hashtags'; import * as langmap from 'langmap'; import { updateHashtag } from '../../../../services/update-hashtag'; import { ApiError } from '../../error'; -import { Users, DriveFiles } from '../../../../models'; +import { Users, DriveFiles, UserProfiles } from '../../../../models'; +import { User } from '../../../../models/entities/user'; +import { UserProfile } from '../../../../models/entities/user-profile'; export const meta = { desc: { @@ -154,22 +156,23 @@ export const meta = { export default define(meta, async (ps, user, app) => { const isSecure = user != null && app == null; - const updates = {} as any; + const updates = {} as Partial; + const profile = {} as Partial; if (ps.name !== undefined) updates.name = ps.name; - if (ps.description !== undefined) updates.description = ps.description; - if (ps.lang !== undefined) updates.lang = ps.lang; - if (ps.location !== undefined) updates.location = ps.location; - if (ps.birthday !== undefined) updates.birthday = ps.birthday; + if (ps.description !== undefined) profile.description = ps.description; + //if (ps.lang !== undefined) updates.lang = ps.lang; + if (ps.location !== undefined) profile.location = ps.location; + if (ps.birthday !== undefined) profile.birthday = ps.birthday; if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked; if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot; - if (typeof ps.carefulBot == 'boolean') updates.carefulBot = ps.carefulBot; - if (typeof ps.autoAcceptFollowed == 'boolean') updates.autoAcceptFollowed = ps.autoAcceptFollowed; + if (typeof ps.carefulBot == 'boolean') profile.carefulBot = ps.carefulBot; + if (typeof ps.autoAcceptFollowed == 'boolean') profile.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat; - if (typeof ps.autoWatch == 'boolean') updates.autoWatch = ps.autoWatch; - if (typeof ps.alwaysMarkNsfw == 'boolean') updates.alwaysMarkNsfw = ps.alwaysMarkNsfw; + if (typeof ps.autoWatch == 'boolean') profile.autoWatch = ps.autoWatch; + if (typeof ps.alwaysMarkNsfw == 'boolean') profile.alwaysMarkNsfw = ps.alwaysMarkNsfw; if (ps.avatarId) { const avatar = await DriveFiles.findOne(ps.avatarId); @@ -206,8 +209,8 @@ export default define(meta, async (ps, user, app) => { emojis = emojis.concat(extractEmojis(tokens)); } - if (updates.description != null) { - const tokens = parse(updates.description); + if (profile.description != null) { + const tokens = parse(profile.description); emojis = emojis.concat(extractEmojis(tokens)); tags = extractHashtags(tokens).map(tag => tag.toLowerCase()); } @@ -221,6 +224,7 @@ export default define(meta, async (ps, user, app) => { //#endregion await Users.update(user.id, updates); + await UserProfiles.update({ userId: user.id }, profile); const iObj = await Users.pack(user.id, user, { detail: true, diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index 7d0ed6e4f9..d868234dc9 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -10,7 +10,7 @@ import { deliver } from '../../../../../queue'; import { renderActivity } from '../../../../../remote/activitypub/renderer'; import renderVote from '../../../../../remote/activitypub/renderer/vote'; import { deliverQuestionUpdate } from '../../../../../services/note/polls/update'; -import { PollVotes, NoteWatchings, Users, Polls } from '../../../../../models'; +import { PollVotes, NoteWatchings, Users, Polls, UserProfiles } from '../../../../../models'; import { Not } from 'typeorm'; import { IRemoteUser } from '../../../../../models/entities/user'; import { genId } from '../../../../../misc/gen-id'; @@ -149,8 +149,10 @@ export default define(meta, async (ps, user) => { } }); + const profile = await UserProfiles.findOne({ userId: user.id }); + // この投稿をWatchする - if (user.autoWatch !== false) { + if (profile.autoWatch !== false) { watch(user.id, note); } diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index c1fd908d8a..fe2e5577c2 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -4,7 +4,7 @@ import * as speakeasy from 'speakeasy'; import { publishMainStream } from '../../../services/stream'; import signin from '../common/signin'; import config from '../../../config'; -import { Users, Signins } from '../../../models'; +import { Users, Signins, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; import { genId } from '../../../misc/gen-id'; @@ -45,13 +45,15 @@ export default async (ctx: Koa.BaseContext) => { return; } + const profile = await UserProfiles.findOne({ userId: user.id }); + // Compare password - const same = await bcrypt.compare(password, user.password); + const same = await bcrypt.compare(password, profile.password); if (same) { - if (user.twoFactorEnabled) { + if (profile.twoFactorEnabled) { const verified = (speakeasy as any).totp.verify({ - secret: user.twoFactorSecret, + secret: profile.twoFactorSecret, encoding: 'base32', token: token }); diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index 657e54decd..5ed25fa411 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -5,13 +5,13 @@ import generateUserToken from '../common/generate-native-user-token'; import config from '../../../config'; import fetchMeta from '../../../misc/fetch-meta'; import * as recaptcha from 'recaptcha-promise'; -import { Users, RegistrationTickets, UserServiceLinkings, UserKeypairs } from '../../../models'; +import { Users, RegistrationTickets, UserProfiles, UserKeypairs } from '../../../models'; import { genId } from '../../../misc/gen-id'; import { usersChart } from '../../../services/chart'; -import { UserServiceLinking } from '../../../models/entities/user-service-linking'; import { User } from '../../../models/entities/user'; import { UserKeypair } from '../../../models/entities/user-keypair'; import { toPuny } from '../../../misc/convert-host'; +import { UserProfile } from '../../../models/entities/user-profile'; export default async (ctx: Koa.BaseContext) => { const body = ctx.request.body as any; @@ -106,23 +106,21 @@ export default async (ctx: Koa.BaseContext) => { usernameLower: username.toLowerCase(), host: toPuny(host), token: secret, - password: hash, isAdmin: config.autoAdmin && usersCount === 0, - autoAcceptFollowed: true, - autoWatch: false } as User); await UserKeypairs.save({ - id: genId(), publicKey: keyPair[0], privateKey: keyPair[1], userId: account.id } as UserKeypair); - await UserServiceLinkings.save({ - id: genId(), - userId: account.id - } as UserServiceLinking); + await UserProfiles.save({ + userId: account.id, + autoAcceptFollowed: true, + autoWatch: false, + password: hash, + } as Partial); usersChart.update(account, true); diff --git a/src/server/api/service/discord.ts b/src/server/api/service/discord.ts index 4290e1ff9d..879b8b4849 100644 --- a/src/server/api/service/discord.ts +++ b/src/server/api/service/discord.ts @@ -8,7 +8,7 @@ import redis from '../../../db/redis'; import * as uuid from 'uuid'; import signin from '../common/signin'; import fetchMeta from '../../../misc/fetch-meta'; -import { Users, UserServiceLinkings } from '../../../models'; +import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; function getUserToken(ctx: Koa.BaseContext) { @@ -45,7 +45,7 @@ router.get('/disconnect/discord', async ctx => { token: userToken }); - await UserServiceLinkings.update({ + await UserProfiles.update({ userId: user.id }, { discord: false, @@ -202,7 +202,7 @@ router.get('/dc/cb', async ctx => { return; } - const link = await UserServiceLinkings.createQueryBuilder() + const profile = await UserProfiles.createQueryBuilder() .where('discord @> :discord', { discord: { id: id, @@ -211,12 +211,12 @@ router.get('/dc/cb', async ctx => { .andWhere('userHost IS NULL') .getOne(); - if (link == null) { + if (profile == null) { ctx.throw(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`); return; } - await UserServiceLinkings.update(link.id, { + await UserProfiles.update({ userId: profile.userId }, { discord: true, discordAccessToken: accessToken, discordRefreshToken: refreshToken, @@ -225,7 +225,7 @@ router.get('/dc/cb', async ctx => { discordDiscriminator: discriminator }); - signin(ctx, await Users.findOne(link.userId) as ILocalUser, true); + signin(ctx, await Users.findOne(profile.userId) as ILocalUser, true); } else { const code = ctx.query.code; @@ -289,7 +289,7 @@ router.get('/dc/cb', async ctx => { token: userToken }); - await UserServiceLinkings.update({ userId: user.id }, { + await UserProfiles.update({ userId: user.id }, { discord: true, discordAccessToken: accessToken, discordRefreshToken: refreshToken, diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index e59b149d19..580947811b 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -8,7 +8,7 @@ import redis from '../../../db/redis'; import * as uuid from 'uuid'; import signin from '../common/signin'; import fetchMeta from '../../../misc/fetch-meta'; -import { Users, UserServiceLinkings } from '../../../models'; +import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; function getUserToken(ctx: Koa.BaseContext) { @@ -45,7 +45,7 @@ router.get('/disconnect/github', async ctx => { token: userToken }); - await UserServiceLinkings.update({ + await UserProfiles.update({ userId: user.id }, { github: false, @@ -191,7 +191,7 @@ router.get('/gh/cb', async ctx => { return; } - const link = await UserServiceLinkings.createQueryBuilder() + const link = await UserProfiles.createQueryBuilder() .where('github @> :github', { github: { id: id, @@ -263,7 +263,7 @@ router.get('/gh/cb', async ctx => { token: userToken }); - await UserServiceLinkings.update({ userId: user.id }, { + await UserProfiles.update({ userId: user.id }, { github: true, githubAccessToken: accessToken, githubId: id, diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts index 77cf71395b..c0c762c6c3 100644 --- a/src/server/api/service/twitter.ts +++ b/src/server/api/service/twitter.ts @@ -7,7 +7,7 @@ import { publishMainStream } from '../../../services/stream'; import config from '../../../config'; import signin from '../common/signin'; import fetchMeta from '../../../misc/fetch-meta'; -import { Users, UserServiceLinkings } from '../../../models'; +import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; function getUserToken(ctx: Koa.BaseContext) { @@ -44,7 +44,7 @@ router.get('/disconnect/twitter', async ctx => { token: userToken }); - await UserServiceLinkings.update({ + await UserProfiles.update({ userId: user.id }, { twitter: false, @@ -139,7 +139,7 @@ router.get('/tw/cb', async ctx => { const result = await twAuth.done(JSON.parse(twCtx), ctx.query.oauth_verifier); - const link = await UserServiceLinkings.createQueryBuilder() + const link = await UserProfiles.createQueryBuilder() .where('twitter @> :twitter', { twitter: { userId: result.userId, @@ -177,7 +177,7 @@ router.get('/tw/cb', async ctx => { token: userToken }); - await UserServiceLinkings.update({ userId: user.id }, { + await UserProfiles.update({ userId: user.id }, { twitter: true, twitterAccessToken: result.accessToken, twitterAccessTokenSecret: result.accessTokenSecret, diff --git a/src/server/index.ts b/src/server/index.ts index 563117773e..9c153f0167 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -23,7 +23,7 @@ import apiServer from './api'; import { sum } from '../prelude/array'; import Logger from '../services/logger'; import { program } from '../argv'; -import { Users } from '../models'; +import { UserProfiles } from '../models'; import { networkChart } from '../services/chart'; export const serverLogger = new Logger('server', 'gray', false); @@ -73,15 +73,15 @@ router.use(nodeinfo.routes()); router.use(wellKnown.routes()); router.get('/verify-email/:code', async ctx => { - const user = await Users.findOne({ + const profile = await UserProfiles.findOne({ emailVerifyCode: ctx.params.code }); - if (user != null) { + if (profile != null) { ctx.body = 'Verify succeeded!'; ctx.status = 200; - Users.update(user.id, { + UserProfiles.update({ userId: profile.userId }, { emailVerified: true, emailVerifyCode: null }); diff --git a/src/server/web/feed.ts b/src/server/web/feed.ts index 4b4ea87973..94f0643f75 100644 --- a/src/server/web/feed.ts +++ b/src/server/web/feed.ts @@ -1,7 +1,7 @@ import { Feed } from 'feed'; import config from '../../config'; import { User } from '../../models/entities/user'; -import { Notes, DriveFiles } from '../../models'; +import { Notes, DriveFiles, UserProfiles } from '../../models'; import { In } from 'typeorm'; export default async function(user: User) { @@ -10,6 +10,8 @@ export default async function(user: User) { name: user.name || user.username }; + const profile = await UserProfiles.findOne({ userId: user.id }); + const notes = await Notes.find({ where: { userId: user.id, @@ -25,7 +27,7 @@ export default async function(user: User) { title: `${author.name} (@${user.username}@${config.host})`, updated: notes[0].createdAt, generator: 'Misskey', - description: `${user.notesCount} Notes, ${user.followingCount} Following, ${user.followersCount} Followers${user.description ? ` · ${user.description}` : ''}`, + description: `${user.notesCount} Notes, ${user.followingCount} Following, ${user.followersCount} Followers${profile.description ? ` · ${profile.description}` : ''}`, link: author.link, image: user.avatarUrl, feedLinks: { diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index 0a84b88fb8..b83c3558d3 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -15,7 +15,7 @@ import { driveLogger } from './logger'; import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor'; import { contentDisposition } from '../../misc/content-disposition'; import { detectMine } from '../../misc/detect-mine'; -import { DriveFiles, DriveFolders, Users, Instances } from '../../models'; +import { DriveFiles, DriveFolders, Users, Instances, UserProfiles } from '../../models'; import { InternalStorage } from './internal-storage'; import { DriveFile } from '../../models/entities/drive-file'; import { IRemoteUser, User } from '../../models/entities/user'; @@ -365,6 +365,8 @@ export default async function( propPromises = [calcWh(), calcAvg()]; } + const profile = await UserProfiles.findOne({ userId: user.id }); + const [folder] = await Promise.all([fetchFolder(), Promise.all(propPromises)]); let file = new DriveFile(); @@ -376,7 +378,7 @@ export default async function( file.comment = comment; file.properties = properties; file.isLink = isLink; - file.isSensitive = Users.isLocalUser(user) && user.alwaysMarkNsfw ? true : + file.isSensitive = Users.isLocalUser(user) && profile.alwaysMarkNsfw ? true : (sensitive !== null && sensitive !== undefined) ? sensitive : false; diff --git a/src/services/following/create.ts b/src/services/following/create.ts index 28e4ba3c12..57bb61fd92 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -9,7 +9,7 @@ import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; import Logger from '../logger'; import { IdentifiableError } from '../../misc/identifiable-error'; import { User } from '../../models/entities/user'; -import { Followings, Users, FollowRequests, Blockings, Instances } from '../../models'; +import { Followings, Users, FollowRequests, Blockings, Instances, UserProfiles } from '../../models'; import { instanceChart, perUserFollowingChart } from '../chart'; import { genId } from '../../misc/gen-id'; import { createNotification } from '../create-notification'; @@ -115,11 +115,13 @@ export default async function(follower: User, followee: User, requestId?: string if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); } + const followeeProfile = await UserProfiles.findOne({ userId: followee.id }); + // フォロー対象が鍵アカウントである or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく - if (followee.isLocked || (followee.carefulBot && follower.isBot) || (Users.isLocalUser(follower) && Users.isRemoteUser(followee))) { + if (followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (Users.isLocalUser(follower) && Users.isRemoteUser(followee))) { let autoAccept = false; // 鍵アカウントであっても、既にフォローされていた場合はスルー @@ -132,7 +134,7 @@ export default async function(follower: User, followee: User, requestId?: string } // フォローしているユーザーは自動承認オプション - if (!autoAccept && (Users.isLocalUser(followee) && followee.autoAcceptFollowed)) { + if (!autoAccept && (Users.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { const followed = await Followings.findOne({ followerId: followee.id, followeeId: follower.id diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 05837a4daf..6058fada16 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -17,7 +17,7 @@ import extractMentions from '../../misc/extract-mentions'; import extractEmojis from '../../misc/extract-emojis'; import extractHashtags from '../../misc/extract-hashtags'; import { Note } from '../../models/entities/note'; -import { Mutings, Users, NoteWatchings, Followings, Notes, Instances, Polls } from '../../models'; +import { Mutings, Users, NoteWatchings, Followings, Notes, Instances, Polls, UserProfiles } from '../../models'; import { DriveFile } from '../../models/entities/drive-file'; import { App } from '../../models/entities/app'; import { Not } from 'typeorm'; @@ -256,13 +256,15 @@ export default async (user: User, data: Option, silent = false) => new Promise new Promise new Promise(async (re } }); + const profile = await UserProfiles.findOne({ userId: user.id }); + // ローカルユーザーが投票した場合この投稿をWatchする - if (Users.isLocalUser(user) && user.autoWatch) { + if (Users.isLocalUser(user) && profile.autoWatch) { watch(user.id, note); } }); diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 437b213ded..1b026cc9cc 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -8,7 +8,7 @@ import { toDbReaction } from '../../../misc/reaction-lib'; import fetchMeta from '../../../misc/fetch-meta'; import { User } from '../../../models/entities/user'; import { Note } from '../../../models/entities/note'; -import { NoteReactions, Users, NoteWatchings, Notes } from '../../../models'; +import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles } from '../../../models'; import { Not } from 'typeorm'; import { perUserReactionsChart } from '../../chart'; import { genId } from '../../../misc/gen-id'; @@ -79,8 +79,10 @@ export default async (user: User, note: Note, reaction: string) => { } }); + const profile = await UserProfiles.findOne({ userId: user.id }); + // ユーザーがローカルユーザーかつ自動ウォッチ設定がオンならばこの投稿をWatchする - if (Users.isLocalUser(user) && user.autoWatch !== false) { + if (Users.isLocalUser(user) && profile.autoWatch) { watch(user.id, note); } -- cgit v1.2.3-freya From 4152e59638606790b4d3069e9360e129068c361c Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 10 Apr 2019 18:05:39 +0900 Subject: Fix bug --- src/server/api/endpoints/i/update-client-setting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/server/api/endpoints/i') diff --git a/src/server/api/endpoints/i/update-client-setting.ts b/src/server/api/endpoints/i/update-client-setting.ts index 49bcb35ae6..36de183379 100644 --- a/src/server/api/endpoints/i/update-client-setting.ts +++ b/src/server/api/endpoints/i/update-client-setting.ts @@ -26,7 +26,7 @@ export default define(meta, async (ps, user) => { [ps.name]: ps.value }, }) - .where('id = :id', { id: user.id }) + .where('userId = :id', { id: user.id }) .execute(); // Publish event -- cgit v1.2.3-freya From 2e3dd2a30a1f3cd6e810c9b6f067ef82b4e4d2f4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 10 Apr 2019 18:13:33 +0900 Subject: Fix bug --- src/server/api/endpoints/i/update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/server/api/endpoints/i') diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index ffc90b2f51..3cb514d939 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -223,8 +223,8 @@ export default define(meta, async (ps, user, app) => { for (const tag of user.tags.filter(x => !tags.includes(x))) updateHashtag(user, tag, true, false); //#endregion - await Users.update(user.id, updates); - await UserProfiles.update({ userId: user.id }, profile); + if (Object.keys(updates).length > 0) await Users.update(user.id, updates); + if (Object.keys(profile).length > 0) await UserProfiles.update({ userId: user.id }, profile); const iObj = await Users.pack(user.id, user, { detail: true, -- cgit v1.2.3-freya From 987168b863c52d0548050ffbac569782bb9a8cef Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 13 Apr 2019 01:43:22 +0900 Subject: strictNullChecks (#4666) * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip --- gulpfile.ts | 2 +- package.json | 2 +- src/boot/master.ts | 2 +- src/boot/worker.ts | 2 +- src/config/load.ts | 2 +- src/daemons/notes-stats-child.ts | 2 +- src/db/postgre.ts | 4 +- src/games/reversi/core.ts | 16 +-- src/mfm/fromHtml.ts | 2 - src/mfm/parse.ts | 4 +- src/mfm/toHtml.ts | 2 +- src/misc/acct/type.ts | 2 +- src/misc/convert-host.ts | 3 +- src/misc/detect-mine.ts | 2 +- src/misc/donwload-url.ts | 3 +- src/misc/fetch-meta.ts | 1 - src/misc/fetch-proxy-account.ts | 3 +- src/misc/identifiable-error.ts | 2 +- src/misc/nyaize.ts | 4 +- src/misc/schema.ts | 4 +- src/models/entities/poll.ts | 2 +- src/models/repositories/abuse-user-report.ts | 3 +- src/models/repositories/app.ts | 3 +- src/models/repositories/auth-session.ts | 3 +- src/models/repositories/blocking.ts | 3 +- src/models/repositories/drive-file.ts | 5 +- src/models/repositories/drive-folder.ts | 3 +- src/models/repositories/follow-request.ts | 3 +- src/models/repositories/following.ts | 43 ++++++- src/models/repositories/games/reversi/game.ts | 3 +- src/models/repositories/games/reversi/matching.ts | 3 +- src/models/repositories/messaging-message.ts | 3 +- src/models/repositories/muting.ts | 3 +- src/models/repositories/note-favorite.ts | 3 +- src/models/repositories/note-reaction.ts | 3 +- src/models/repositories/note.ts | 17 +-- src/models/repositories/notification.ts | 15 +-- src/models/repositories/user-list.ts | 5 +- src/models/repositories/user.ts | 29 ++--- src/prelude/ensure.ts | 7 ++ src/queue/index.ts | 4 +- src/queue/processors/db/delete-drive-files.ts | 8 +- src/queue/processors/db/export-blocking.ts | 12 +- src/queue/processors/db/export-following.ts | 12 +- src/queue/processors/db/export-mute.ts | 12 +- src/queue/processors/db/export-notes.ts | 15 ++- src/queue/processors/db/export-user-lists.ts | 8 +- src/queue/processors/db/import-following.ts | 16 ++- src/queue/processors/db/import-user-lists.ts | 22 ++-- src/queue/processors/deliver.ts | 2 +- src/queue/processors/inbox.ts | 9 +- src/remote/activitypub/kernel/accept/follow.ts | 3 +- src/remote/activitypub/kernel/add/index.ts | 1 + src/remote/activitypub/kernel/announce/note.ts | 9 +- src/remote/activitypub/kernel/block/index.ts | 3 +- src/remote/activitypub/kernel/follow.ts | 3 +- src/remote/activitypub/kernel/index.ts | 2 +- src/remote/activitypub/kernel/like.ts | 1 + src/remote/activitypub/kernel/reject/follow.ts | 3 +- src/remote/activitypub/kernel/remove/index.ts | 1 + src/remote/activitypub/kernel/undo/block.ts | 3 +- src/remote/activitypub/kernel/undo/follow.ts | 3 +- src/remote/activitypub/kernel/undo/index.ts | 2 - src/remote/activitypub/kernel/undo/like.ts | 1 + src/remote/activitypub/models/image.ts | 17 +-- src/remote/activitypub/models/note.ts | 130 +++++++++++---------- src/remote/activitypub/models/person.ts | 81 ++++++------- src/remote/activitypub/models/question.ts | 8 +- src/remote/activitypub/models/tag.ts | 8 +- src/remote/activitypub/renderer/block.ts | 2 +- src/remote/activitypub/renderer/follow-user.ts | 3 +- src/remote/activitypub/renderer/note.ts | 19 ++- .../renderer/ordered-collection-page.ts | 2 +- src/remote/activitypub/renderer/person.ts | 15 ++- src/remote/activitypub/request.ts | 9 +- src/remote/resolve-user.ts | 31 ++++- src/remote/webfinger.ts | 2 +- src/server/activitypub.ts | 13 +-- src/server/activitypub/featured.ts | 6 +- src/server/activitypub/followers.ts | 6 +- src/server/activitypub/following.ts | 6 +- src/server/activitypub/outbox.ts | 9 +- src/server/api/api-handler.ts | 14 +-- src/server/api/authenticate.ts | 2 +- src/server/api/call.ts | 10 +- src/server/api/common/make-pagination-query.ts | 2 +- src/server/api/define.ts | 10 +- .../api/endpoints/admin/abuse-user-reports.ts | 2 +- src/server/api/endpoints/admin/drive/files.ts | 4 +- src/server/api/endpoints/admin/emoji/list.ts | 2 +- .../admin/federation/remove-all-following.ts | 7 +- src/server/api/endpoints/admin/logs.ts | 2 +- src/server/api/endpoints/admin/queue/jobs.ts | 4 +- src/server/api/endpoints/admin/show-users.ts | 2 +- .../api/endpoints/admin/update-remote-user.ts | 2 +- src/server/api/endpoints/ap/show.ts | 4 +- src/server/api/endpoints/auth/accept.ts | 3 +- src/server/api/endpoints/auth/session/userkey.ts | 3 +- src/server/api/endpoints/blocking/list.ts | 2 +- src/server/api/endpoints/charts/active-users.ts | 2 +- src/server/api/endpoints/charts/drive.ts | 2 +- src/server/api/endpoints/charts/federation.ts | 2 +- src/server/api/endpoints/charts/hashtag.ts | 2 +- src/server/api/endpoints/charts/instance.ts | 2 +- src/server/api/endpoints/charts/network.ts | 2 +- src/server/api/endpoints/charts/notes.ts | 2 +- src/server/api/endpoints/charts/user/drive.ts | 2 +- src/server/api/endpoints/charts/user/following.ts | 2 +- src/server/api/endpoints/charts/user/notes.ts | 2 +- src/server/api/endpoints/charts/user/reactions.ts | 2 +- src/server/api/endpoints/charts/users.ts | 2 +- src/server/api/endpoints/drive/files.ts | 2 +- src/server/api/endpoints/drive/files/create.ts | 2 +- src/server/api/endpoints/drive/files/show.ts | 10 +- src/server/api/endpoints/drive/folders.ts | 2 +- src/server/api/endpoints/drive/folders/update.ts | 6 +- src/server/api/endpoints/drive/stream.ts | 2 +- src/server/api/endpoints/federation/instances.ts | 2 +- src/server/api/endpoints/games/reversi/games.ts | 2 +- src/server/api/endpoints/games/reversi/match.ts | 4 +- src/server/api/endpoints/hashtags/list.ts | 2 +- src/server/api/endpoints/hashtags/search.ts | 2 +- src/server/api/endpoints/hashtags/users.ts | 2 +- src/server/api/endpoints/i/2fa/done.ts | 3 +- src/server/api/endpoints/i/2fa/register.ts | 5 +- src/server/api/endpoints/i/2fa/unregister.ts | 5 +- src/server/api/endpoints/i/authorized-apps.ts | 2 +- src/server/api/endpoints/i/change-password.ts | 5 +- src/server/api/endpoints/i/delete-account.ts | 5 +- src/server/api/endpoints/i/favorites.ts | 2 +- src/server/api/endpoints/i/notifications.ts | 6 +- src/server/api/endpoints/i/regenerate-token.ts | 5 +- src/server/api/endpoints/i/signin-history.ts | 2 +- src/server/api/endpoints/i/update-email.ts | 5 +- src/server/api/endpoints/i/update.ts | 6 +- src/server/api/endpoints/messaging/history.ts | 2 +- src/server/api/endpoints/mute/list.ts | 2 +- src/server/api/endpoints/my/apps.ts | 2 +- src/server/api/endpoints/notes.ts | 2 +- src/server/api/endpoints/notes/children.ts | 2 +- src/server/api/endpoints/notes/conversation.ts | 5 +- src/server/api/endpoints/notes/create.ts | 27 ++--- src/server/api/endpoints/notes/delete.ts | 3 +- src/server/api/endpoints/notes/featured.ts | 2 +- src/server/api/endpoints/notes/global-timeline.ts | 2 +- src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- src/server/api/endpoints/notes/local-timeline.ts | 8 +- src/server/api/endpoints/notes/mentions.ts | 2 +- .../api/endpoints/notes/polls/recommendation.ts | 2 +- src/server/api/endpoints/notes/polls/vote.ts | 7 +- src/server/api/endpoints/notes/reactions.ts | 2 +- src/server/api/endpoints/notes/renotes.ts | 2 +- src/server/api/endpoints/notes/replies.ts | 2 +- src/server/api/endpoints/notes/search-by-tag.ts | 4 +- src/server/api/endpoints/notes/search.ts | 2 +- src/server/api/endpoints/notes/timeline.ts | 2 +- .../api/endpoints/notes/user-list-timeline.ts | 2 +- src/server/api/endpoints/users.ts | 2 +- src/server/api/endpoints/users/followers.ts | 4 +- src/server/api/endpoints/users/following.ts | 4 +- .../users/get-frequently-replied-users.ts | 2 +- src/server/api/endpoints/users/notes.ts | 8 +- src/server/api/endpoints/users/recommendation.ts | 2 +- src/server/api/endpoints/users/search.ts | 6 +- src/server/api/endpoints/users/show.ts | 6 +- src/server/api/error.ts | 4 +- src/server/api/limiter.ts | 6 +- src/server/api/private/signin.ts | 5 +- src/server/api/private/signup.ts | 4 +- src/server/api/service/discord.ts | 91 ++++++++------- src/server/api/service/github.ts | 45 ++++--- src/server/api/service/twitter.ts | 25 ++-- src/server/api/stream/channels/admin.ts | 2 +- src/server/api/stream/channels/drive.ts | 2 +- .../api/stream/channels/games/reversi-game.ts | 80 +++++++------ src/server/api/stream/channels/games/reversi.ts | 4 +- src/server/api/stream/channels/home-timeline.ts | 8 +- src/server/api/stream/channels/hybrid-timeline.ts | 10 +- src/server/api/stream/channels/main.ts | 4 +- src/server/api/stream/channels/messaging-index.ts | 2 +- src/server/api/stream/channels/messaging.ts | 4 +- src/server/api/stream/index.ts | 20 ++-- src/server/file/send-drive-file.ts | 2 +- src/server/index.ts | 2 +- src/server/web/docs.ts | 6 +- src/server/web/feed.ts | 9 +- src/server/web/index.ts | 5 +- src/server/web/url-preview.ts | 2 +- src/server/well-known.ts | 4 +- src/services/blocking/create.ts | 2 +- src/services/chart/core.ts | 36 +++--- src/services/create-notification.ts | 1 + src/services/drive/add-file.ts | 36 +++--- src/services/drive/delete-file.ts | 16 +-- src/services/drive/image-processor.ts | 2 +- src/services/drive/upload-from-url.ts | 11 +- src/services/following/create.ts | 3 +- src/services/following/requests/accept-all.ts | 3 +- src/services/following/requests/accept.ts | 2 +- src/services/following/requests/reject.ts | 2 +- src/services/i/pin.ts | 15 +-- src/services/i/update.ts | 7 +- src/services/logger.ts | 16 +-- src/services/note/create.ts | 101 ++++++++-------- src/services/note/polls/update.ts | 11 +- src/services/note/polls/vote.ts | 16 +-- src/services/note/reaction/create.ts | 4 +- src/services/note/reaction/delete.ts | 2 +- src/services/push-notification.ts | 23 ++-- src/services/register-or-fetch-instance-doc.ts | 2 - src/services/stream.ts | 6 +- src/tools/clean-remote-files.ts | 3 +- src/tools/show-signin-history.ts | 4 +- tsconfig.json | 3 +- 214 files changed, 940 insertions(+), 786 deletions(-) create mode 100644 src/prelude/ensure.ts (limited to 'src/server/api/endpoints/i') diff --git a/gulpfile.ts b/gulpfile.ts index b2956c2403..2242843db1 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -120,7 +120,7 @@ gulp.task('copy:client', () => ]) .pipe(isProduction ? (imagemin as any)() : gutil.noop()) .pipe(rename(path => { - path.dirname = path.dirname.replace('assets', '.'); + path.dirname = path.dirname!.replace('assets', '.'); })) .pipe(gulp.dest('./built/client/assets/')) ); diff --git a/package.json b/package.json index e89f98043c..e08a49462d 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "bcryptjs": "2.4.3", "bootstrap-vue": "2.0.0-rc.13", "bull": "3.7.0", - "cafy": "15.1.0", + "cafy": "15.1.1", "chai": "4.2.0", "chai-http": "4.2.1", "chalk": "2.4.2", diff --git a/src/boot/master.ts b/src/boot/master.ts index 2d4080fdb0..4d360c7265 100644 --- a/src/boot/master.ts +++ b/src/boot/master.ts @@ -44,7 +44,7 @@ function greet() { export async function masterMain() { greet(); - let config: Config; + let config!: Config; try { // initialize app diff --git a/src/boot/worker.ts b/src/boot/worker.ts index ca3716972a..362fa3f26b 100644 --- a/src/boot/worker.ts +++ b/src/boot/worker.ts @@ -15,6 +15,6 @@ export async function workerMain() { if (cluster.isWorker) { // Send a 'ready' message to parent process - process.send('ready'); + process.send!('ready'); } } diff --git a/src/config/load.ts b/src/config/load.ts index 6200faf12a..26b25eab4e 100644 --- a/src/config/load.ts +++ b/src/config/load.ts @@ -29,7 +29,7 @@ export default function load() { config.url = url.origin; - config.port = config.port || parseInt(process.env.PORT, 10); + config.port = config.port || parseInt(process.env.PORT || '', 10); mixin.host = url.host; mixin.hostname = url.hostname; diff --git a/src/daemons/notes-stats-child.ts b/src/daemons/notes-stats-child.ts index c491aed4cd..b60f5badfd 100644 --- a/src/daemons/notes-stats-child.ts +++ b/src/daemons/notes-stats-child.ts @@ -19,7 +19,7 @@ initDb().then(() => { all, local }; - process.send(stats); + process.send!(stats); } tick(); diff --git a/src/db/postgre.ts b/src/db/postgre.ts index e5726e9c87..641a552c09 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -76,7 +76,7 @@ class MyCustomLogger implements Logger { } export function initDb(justBorrow = false, sync = false, log = false) { - const enableLogging = log || !['production', 'test'].includes(process.env.NODE_ENV); + const enableLogging = log || !['production', 'test'].includes(process.env.NODE_ENV || ''); try { const conn = getConnection(); @@ -93,7 +93,7 @@ export function initDb(justBorrow = false, sync = false, log = false) { synchronize: process.env.NODE_ENV === 'test' || sync, dropSchema: process.env.NODE_ENV === 'test' && !justBorrow, logging: enableLogging, - logger: enableLogging ? new MyCustomLogger() : null, + logger: enableLogging ? new MyCustomLogger() : undefined, entities: [ Meta, Instance, diff --git a/src/games/reversi/core.ts b/src/games/reversi/core.ts index bb27d6f803..cf8986263b 100644 --- a/src/games/reversi/core.ts +++ b/src/games/reversi/core.ts @@ -37,7 +37,7 @@ export type Undo = { /** * ターン */ - turn: Color; + turn: Color | null; }; /** @@ -47,12 +47,12 @@ export default class Reversi { public map: MapPixel[]; public mapWidth: number; public mapHeight: number; - public board: Color[]; - public turn: Color = BLACK; + public board: (Color | null | undefined)[]; + public turn: Color | null = BLACK; public opts: Options; public prevPos = -1; - public prevColor: Color = null; + public prevColor: Color | null = null; private logs: Undo[] = []; @@ -145,12 +145,12 @@ export default class Reversi { // ターン計算 this.turn = this.canPutSomewhere(!this.prevColor) ? !this.prevColor : - this.canPutSomewhere(this.prevColor) ? this.prevColor : + this.canPutSomewhere(this.prevColor!) ? this.prevColor : null; } public undo() { - const undo = this.logs.pop(); + const undo = this.logs.pop()!; this.prevColor = undo.color; this.prevPos = undo.pos; this.board[undo.pos] = null; @@ -254,10 +254,10 @@ export default class Reversi { /** * ゲームの勝者 (null = 引き分け) */ - public get winner(): Color { + public get winner(): Color | null { return this.isEnded ? this.blackCount == this.whiteCount ? null : this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK : - undefined; + undefined as never; } } diff --git a/src/mfm/fromHtml.ts b/src/mfm/fromHtml.ts index 369f9de0ee..5fc4a16416 100644 --- a/src/mfm/fromHtml.ts +++ b/src/mfm/fromHtml.ts @@ -3,8 +3,6 @@ import { URL } from 'url'; import { urlRegex } from './prelude'; export function fromHtml(html: string): string { - if (html == null) return null; - const dom = parseFragment(html) as DefaultTreeDocumentFragment; let text = ''; diff --git a/src/mfm/parse.ts b/src/mfm/parse.ts index 9d60771708..f8464121f3 100644 --- a/src/mfm/parse.ts +++ b/src/mfm/parse.ts @@ -2,7 +2,7 @@ import { mfmLanguage } from './language'; import { MfmForest } from './prelude'; import { normalize } from './normalize'; -export function parse(source: string): MfmForest { +export function parse(source: string | null): MfmForest | null { if (source == null || source == '') { return null; } @@ -10,7 +10,7 @@ export function parse(source: string): MfmForest { return normalize(mfmLanguage.root.tryParse(source)); } -export function parsePlain(source: string): MfmForest { +export function parsePlain(source: string | null): MfmForest | null { if (source == null || source == '') { return null; } diff --git a/src/mfm/toHtml.ts b/src/mfm/toHtml.ts index 3cd7987624..58976fc2c3 100644 --- a/src/mfm/toHtml.ts +++ b/src/mfm/toHtml.ts @@ -4,7 +4,7 @@ import { intersperse } from '../prelude/array'; import { MfmForest, MfmTree } from './prelude'; import { IMentionedRemoteUsers } from '../models/entities/note'; -export function toHtml(tokens: MfmForest, mentionedRemoteUsers: IMentionedRemoteUsers = []) { +export function toHtml(tokens: MfmForest | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { if (tokens == null) { return null; } diff --git a/src/misc/acct/type.ts b/src/misc/acct/type.ts index c88a920c69..7f31257400 100644 --- a/src/misc/acct/type.ts +++ b/src/misc/acct/type.ts @@ -1,6 +1,6 @@ type Acct = { username: string; - host: string; + host: string | null; }; export default Acct; diff --git a/src/misc/convert-host.ts b/src/misc/convert-host.ts index f7feebd55c..dbf7864550 100644 --- a/src/misc/convert-host.ts +++ b/src/misc/convert-host.ts @@ -2,7 +2,7 @@ import config from '../config'; import { toASCII } from 'punycode'; import { URL } from 'url'; -export function getFullApAccount(username: string, host: string) { +export function getFullApAccount(username: string, host: string | null) { return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`; } @@ -17,6 +17,5 @@ export function extractDbHost(uri: string) { } export function toPuny(host: string) { - if (host == null) return null; return toASCII(host.toLowerCase()); } diff --git a/src/misc/detect-mine.ts b/src/misc/detect-mine.ts index bbf49efc10..70d58ffe21 100644 --- a/src/misc/detect-mine.ts +++ b/src/misc/detect-mine.ts @@ -3,7 +3,7 @@ import fileType from 'file-type'; import checkSvg from '../misc/check-svg'; export async function detectMine(path: string) { - return new Promise<[string, string]>((res, rej) => { + return new Promise<[string, string | null]>((res, rej) => { const readable = fs.createReadStream(path); readable .on('error', rej) diff --git a/src/misc/donwload-url.ts b/src/misc/donwload-url.ts index 0dd4e4ef5d..167e01fdd1 100644 --- a/src/misc/donwload-url.ts +++ b/src/misc/donwload-url.ts @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import * as URL from 'url'; import * as request from 'request'; import config from '../config'; import chalk from 'chalk'; @@ -26,7 +25,7 @@ export async function downloadUrl(url: string, path: string) { rej(error); }); - const requestUrl = URL.parse(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url; + const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url; const req = request({ url: requestUrl, diff --git a/src/misc/fetch-meta.ts b/src/misc/fetch-meta.ts index d1483e9edb..cf1fc474ce 100644 --- a/src/misc/fetch-meta.ts +++ b/src/misc/fetch-meta.ts @@ -9,7 +9,6 @@ export default async function(): Promise { } else { return Metas.save({ id: genId(), - hiddenTags: [] } as Meta); } } diff --git a/src/misc/fetch-proxy-account.ts b/src/misc/fetch-proxy-account.ts index d60fa9b313..17b021e91e 100644 --- a/src/misc/fetch-proxy-account.ts +++ b/src/misc/fetch-proxy-account.ts @@ -1,8 +1,9 @@ import fetchMeta from './fetch-meta'; import { ILocalUser } from '../models/entities/user'; import { Users } from '../models'; +import { ensure } from '../prelude/ensure'; export async function fetchProxyAccount(): Promise { const meta = await fetchMeta(); - return await Users.findOne({ username: meta.proxyAccount, host: null }) as ILocalUser; + return await Users.findOne({ username: meta.proxyAccount!, host: null }).then(ensure) as ILocalUser; } diff --git a/src/misc/identifiable-error.ts b/src/misc/identifiable-error.ts index 1edd26cd18..2d7c6bd0c6 100644 --- a/src/misc/identifiable-error.ts +++ b/src/misc/identifiable-error.ts @@ -7,7 +7,7 @@ export class IdentifiableError extends Error { constructor(id: string, message?: string) { super(message); - this.message = message; + this.message = message || ''; this.id = id; } } diff --git a/src/misc/nyaize.ts b/src/misc/nyaize.ts index 8b06300eab..918e7d63fd 100644 --- a/src/misc/nyaize.ts +++ b/src/misc/nyaize.ts @@ -3,7 +3,7 @@ export function nyaize(text: string): string { // ja-JP .replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ') // ko-KR - .replace(/[나-낳]/g, (match: string) => String.fromCharCode( - match.codePointAt(0) + '냐'.charCodeAt(0) - '나'.charCodeAt(0) + .replace(/[나-낳]/g, match => String.fromCharCode( + match.codePointAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0) )); } diff --git a/src/misc/schema.ts b/src/misc/schema.ts index e5c24dd468..7c17953d97 100644 --- a/src/misc/schema.ts +++ b/src/misc/schema.ts @@ -19,8 +19,8 @@ type MyType = { export type SchemaType

= p['type'] extends 'number' ? number : p['type'] extends 'string' ? string : - p['type'] extends 'array' ? MyType[] : - p['type'] extends 'object' ? ObjType : + p['type'] extends 'array' ? MyType>[] : + p['type'] extends 'object' ? ObjType> : any; export function convertOpenApiSchema(schema: Schema) { diff --git a/src/models/entities/poll.ts b/src/models/entities/poll.ts index c0ad5547bd..6bb67163a2 100644 --- a/src/models/entities/poll.ts +++ b/src/models/entities/poll.ts @@ -67,5 +67,5 @@ export type IPoll = { choices: string[]; votes?: number[]; multiple: boolean; - expiresAt: Date; + expiresAt: Date | null; }; diff --git a/src/models/repositories/abuse-user-report.ts b/src/models/repositories/abuse-user-report.ts index c72a582c04..f619d6e37f 100644 --- a/src/models/repositories/abuse-user-report.ts +++ b/src/models/repositories/abuse-user-report.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import rap from '@prezzemolo/rap'; import { AbuseUserReport } from '../entities/abuse-user-report'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(AbuseUserReport) export class AbuseUserReportRepository extends Repository { @@ -14,7 +15,7 @@ export class AbuseUserReportRepository extends Repository { public async pack( src: AbuseUserReport['id'] | AbuseUserReport, ) { - const report = typeof src === 'object' ? src : await this.findOne(src); + const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: report.id, diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts index 2e3323baf8..a0c0cf68cb 100644 --- a/src/models/repositories/app.ts +++ b/src/models/repositories/app.ts @@ -1,6 +1,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { App } from '../entities/app'; import { AccessTokens } from '..'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(App) export class AppRepository extends Repository { @@ -19,7 +20,7 @@ export class AppRepository extends Repository { includeProfileImageIds: false }, options); - const app = typeof src === 'object' ? src : await this.findOne(src); + const app = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: app.id, diff --git a/src/models/repositories/auth-session.ts b/src/models/repositories/auth-session.ts index 76e3ddf9ab..540c5466f5 100644 --- a/src/models/repositories/auth-session.ts +++ b/src/models/repositories/auth-session.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { Apps } from '..'; import rap from '@prezzemolo/rap'; import { AuthSession } from '../entities/auth-session'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(AuthSession) export class AuthSessionRepository extends Repository { @@ -9,7 +10,7 @@ export class AuthSessionRepository extends Repository { src: AuthSession['id'] | AuthSession, me?: any ) { - const session = typeof src === 'object' ? src : await this.findOne(src); + const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: session.id, diff --git a/src/models/repositories/blocking.ts b/src/models/repositories/blocking.ts index 81f3866131..e18aa591f3 100644 --- a/src/models/repositories/blocking.ts +++ b/src/models/repositories/blocking.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import rap from '@prezzemolo/rap'; import { Blocking } from '../entities/blocking'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(Blocking) export class BlockingRepository extends Repository { @@ -16,7 +17,7 @@ export class BlockingRepository extends Repository { src: Blocking['id'] | Blocking, me?: any ) { - const blocking = typeof src === 'object' ? src : await this.findOne(src); + const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: blocking.id, diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts index 817677fa3b..003e350713 100644 --- a/src/models/repositories/drive-file.ts +++ b/src/models/repositories/drive-file.ts @@ -4,6 +4,7 @@ import { Users, DriveFolders } from '..'; import rap from '@prezzemolo/rap'; import { User } from '../entities/user'; import { toPuny } from '../../misc/convert-host'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(DriveFile) export class DriveFileRepository extends Repository { @@ -91,7 +92,7 @@ export class DriveFileRepository extends Repository { self: false }, options); - const file = typeof src === 'object' ? src : await this.findOne(src); + const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: file.id, @@ -108,7 +109,7 @@ export class DriveFileRepository extends Repository { folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { detail: true }) : null, - user: opts.withUser ? Users.pack(file.userId) : null + user: opts.withUser ? Users.pack(file.userId!) : null }); } } diff --git a/src/models/repositories/drive-folder.ts b/src/models/repositories/drive-folder.ts index faf0f353aa..ce88adefa4 100644 --- a/src/models/repositories/drive-folder.ts +++ b/src/models/repositories/drive-folder.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { DriveFolders, DriveFiles } from '..'; import rap from '@prezzemolo/rap'; import { DriveFolder } from '../entities/drive-folder'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(DriveFolder) export class DriveFolderRepository extends Repository { @@ -22,7 +23,7 @@ export class DriveFolderRepository extends Repository { detail: false }, options); - const folder = typeof src === 'object' ? src : await this.findOne(src); + const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: folder.id, diff --git a/src/models/repositories/follow-request.ts b/src/models/repositories/follow-request.ts index bead093b21..451ed8e2d5 100644 --- a/src/models/repositories/follow-request.ts +++ b/src/models/repositories/follow-request.ts @@ -1,6 +1,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { FollowRequest } from '../entities/follow-request'; import { Users } from '..'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(FollowRequest) export class FollowRequestRepository extends Repository { @@ -8,7 +9,7 @@ export class FollowRequestRepository extends Repository { src: FollowRequest['id'] | FollowRequest, me?: any ) { - const request = typeof src === 'object' ? src : await this.findOne(src); + const request = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: request.id, diff --git a/src/models/repositories/following.ts b/src/models/repositories/following.ts index 02253d272d..3fff57866f 100644 --- a/src/models/repositories/following.ts +++ b/src/models/repositories/following.ts @@ -2,9 +2,50 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import rap from '@prezzemolo/rap'; import { Following } from '../entities/following'; +import { ensure } from '../../prelude/ensure'; + +type LocalFollowerFollowing = Following & { + followerHost: null; + followerInbox: null; + followerSharedInbox: null; +}; + +type RemoteFollowerFollowing = Following & { + followerHost: string; + followerInbox: string; + followerSharedInbox: string; +}; + +type LocalFolloweeFollowing = Following & { + followeeHost: null; + followeeInbox: null; + followeeSharedInbox: null; +}; + +type RemoteFolloweeFollowing = Following & { + followeeHost: string; + followeeInbox: string; + followeeSharedInbox: string; +}; @EntityRepository(Following) export class FollowingRepository extends Repository { + public isLocalFollower(following: Following): following is LocalFollowerFollowing { + return following.followerHost == null; + } + + public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { + return following.followerHost != null; + } + + public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { + return following.followeeHost == null; + } + + public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { + return following.followeeHost != null; + } + public packMany( followings: any[], me?: any, @@ -24,7 +65,7 @@ export class FollowingRepository extends Repository { populateFollower?: boolean; } ) { - const following = typeof src === 'object' ? src : await this.findOne(src); + const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure); if (opts == null) opts = {}; diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts index f0cb6ff905..c380f5251e 100644 --- a/src/models/repositories/games/reversi/game.ts +++ b/src/models/repositories/games/reversi/game.ts @@ -1,6 +1,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '../../..'; import { ReversiGame } from '../../../entities/games/reversi/game'; +import { ensure } from '../../../../prelude/ensure'; @EntityRepository(ReversiGame) export class ReversiGameRepository extends Repository { @@ -15,7 +16,7 @@ export class ReversiGameRepository extends Repository { detail: true }, options); - const game = typeof src === 'object' ? src : await this.findOne(src); + const game = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const meId = me ? typeof me === 'string' ? me : me.id : null; return { diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts index 3612ac5c47..4d99c6ef76 100644 --- a/src/models/repositories/games/reversi/matching.ts +++ b/src/models/repositories/games/reversi/matching.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import rap from '@prezzemolo/rap'; import { ReversiMatching } from '../../../entities/games/reversi/matching'; import { Users } from '../../..'; +import { ensure } from '../../../../prelude/ensure'; @EntityRepository(ReversiMatching) export class ReversiMatchingRepository extends Repository { @@ -9,7 +10,7 @@ export class ReversiMatchingRepository extends Repository { src: ReversiMatching['id'] | ReversiMatching, me: any ) { - const matching = typeof src === 'object' ? src : await this.findOne(src); + const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: matching.id, diff --git a/src/models/repositories/messaging-message.ts b/src/models/repositories/messaging-message.ts index b87b30388a..6659273539 100644 --- a/src/models/repositories/messaging-message.ts +++ b/src/models/repositories/messaging-message.ts @@ -1,6 +1,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { MessagingMessage } from '../entities/messaging-message'; import { Users, DriveFiles } from '..'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(MessagingMessage) export class MessagingMessageRepository extends Repository { @@ -19,7 +20,7 @@ export class MessagingMessageRepository extends Repository { populateRecipient: true }; - const message = typeof src === 'object' ? src : await this.findOne(src); + const message = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: message.id, diff --git a/src/models/repositories/muting.ts b/src/models/repositories/muting.ts index cd98cb4fec..1812e2e713 100644 --- a/src/models/repositories/muting.ts +++ b/src/models/repositories/muting.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users } from '..'; import rap from '@prezzemolo/rap'; import { Muting } from '../entities/muting'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(Muting) export class MutingRepository extends Repository { @@ -16,7 +17,7 @@ export class MutingRepository extends Repository { src: Muting['id'] | Muting, me?: any ) { - const muting = typeof src === 'object' ? src : await this.findOne(src); + const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: muting.id, diff --git a/src/models/repositories/note-favorite.ts b/src/models/repositories/note-favorite.ts index 4526461e69..f428903c13 100644 --- a/src/models/repositories/note-favorite.ts +++ b/src/models/repositories/note-favorite.ts @@ -1,6 +1,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { NoteFavorite } from '../entities/note-favorite'; import { Notes } from '..'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(NoteFavorite) export class NoteFavoriteRepository extends Repository { @@ -15,7 +16,7 @@ export class NoteFavoriteRepository extends Repository { src: NoteFavorite['id'] | NoteFavorite, me?: any ) { - const favorite = typeof src === 'object' ? src : await this.findOne(src); + const favorite = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: favorite.id, diff --git a/src/models/repositories/note-reaction.ts b/src/models/repositories/note-reaction.ts index 7189da8e20..28191d4ab0 100644 --- a/src/models/repositories/note-reaction.ts +++ b/src/models/repositories/note-reaction.ts @@ -1,6 +1,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { NoteReaction } from '../entities/note-reaction'; import { Users } from '..'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(NoteReaction) export class NoteReactionRepository extends Repository { @@ -8,7 +9,7 @@ export class NoteReactionRepository extends Repository { src: NoteReaction['id'] | NoteReaction, me?: any ) { - const reaction = typeof src === 'object' ? src : await this.findOne(src); + const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: reaction.id, diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index 01bf8cf87c..77cf00849f 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -5,6 +5,7 @@ import { unique, concat } from '../../prelude/array'; import { nyaize } from '../../misc/nyaize'; import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..'; import rap from '@prezzemolo/rap'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(Note) export class NoteRepository extends Repository { @@ -12,7 +13,7 @@ export class NoteRepository extends Repository { return x.trim().length <= 100; } - private async hideNote(packedNote: any, meId: User['id']) { + private async hideNote(packedNote: any, meId: User['id'] | null) { let hide = false; // visibility が specified かつ自分が指定されていなかったら非表示 @@ -75,7 +76,7 @@ export class NoteRepository extends Repository { public packMany( notes: (Note['id'] | Note)[], - me?: User['id'] | User, + me?: User['id'] | User | null | undefined, options?: { detail?: boolean; skipHide?: boolean; @@ -86,7 +87,7 @@ export class NoteRepository extends Repository { public async pack( src: Note['id'] | Note, - me?: User['id'] | User, + me?: User['id'] | User | null | undefined, options?: { detail?: boolean; skipHide?: boolean; @@ -98,11 +99,11 @@ export class NoteRepository extends Repository { }, options); const meId = me ? typeof me === 'string' ? me : me.id : null; - const note = typeof src === 'object' ? src : await this.findOne(src); + const note = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const host = note.userHost; async function populatePoll() { - const poll = await Polls.findOne({ noteId: note.id }); + const poll = await Polls.findOne({ noteId: note.id }).then(ensure); const choices = poll.choices.map(c => ({ text: c, votes: poll.votes[poll.choices.indexOf(c)], @@ -111,7 +112,7 @@ export class NoteRepository extends Repository { if (poll.multiple) { const votes = await PollVotes.find({ - userId: meId, + userId: meId!, noteId: note.id }); @@ -121,7 +122,7 @@ export class NoteRepository extends Repository { } } else { const vote = await PollVotes.findOne({ - userId: meId, + userId: meId!, noteId: note.id }); @@ -139,7 +140,7 @@ export class NoteRepository extends Repository { async function populateMyReaction() { const reaction = await NoteReactions.findOne({ - userId: meId, + userId: meId!, noteId: note.id, }); diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index 9bc569cd3f..4781d4c065 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; import { Users, Notes } from '..'; import rap from '@prezzemolo/rap'; import { Notification } from '../entities/notification'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(Notification) export class NotificationRepository extends Repository { @@ -14,7 +15,7 @@ export class NotificationRepository extends Repository { public async pack( src: Notification['id'] | Notification, ) { - const notification = typeof src === 'object' ? src : await this.findOne(src); + const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return await rap({ id: notification.id, @@ -23,23 +24,23 @@ export class NotificationRepository extends Repository { userId: notification.notifierId, user: Users.pack(notification.notifier || notification.notifierId), ...(notification.type === 'mention' ? { - note: Notes.pack(notification.note || notification.noteId), + note: Notes.pack(notification.note || notification.noteId!), } : {}), ...(notification.type === 'reply' ? { - note: Notes.pack(notification.note || notification.noteId), + note: Notes.pack(notification.note || notification.noteId!), } : {}), ...(notification.type === 'renote' ? { - note: Notes.pack(notification.note || notification.noteId), + note: Notes.pack(notification.note || notification.noteId!), } : {}), ...(notification.type === 'quote' ? { - note: Notes.pack(notification.note || notification.noteId), + note: Notes.pack(notification.note || notification.noteId!), } : {}), ...(notification.type === 'reaction' ? { - note: Notes.pack(notification.note || notification.noteId), + note: Notes.pack(notification.note || notification.noteId!), reaction: notification.reaction } : {}), ...(notification.type === 'pollVote' ? { - note: Notes.pack(notification.note || notification.noteId), + note: Notes.pack(notification.note || notification.noteId!), choice: notification.choice } : {}) }); diff --git a/src/models/repositories/user-list.ts b/src/models/repositories/user-list.ts index 921c18ca7a..fbf81b8886 100644 --- a/src/models/repositories/user-list.ts +++ b/src/models/repositories/user-list.ts @@ -1,12 +1,13 @@ import { EntityRepository, Repository } from 'typeorm'; import { UserList } from '../entities/user-list'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(UserList) export class UserListRepository extends Repository { public async pack( - src: any, + src: UserList['id'] | UserList, ) { - const userList = typeof src === 'object' ? src : await this.findOne(src); + const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure); return { id: userList.id, diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index d7f2c3d040..cddb77ffae 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -2,6 +2,7 @@ import { EntityRepository, Repository, In } from 'typeorm'; import { User, ILocalUser, IRemoteUser } from '../entities/user'; import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; import rap from '@prezzemolo/rap'; +import { ensure } from '../../prelude/ensure'; @EntityRepository(User) export class UserRepository extends Repository { @@ -51,7 +52,7 @@ export class UserRepository extends Repository { public packMany( users: (User['id'] | User)[], - me?: User['id'] | User, + me?: User['id'] | User | null | undefined, options?: { detail?: boolean, includeSecrets?: boolean, @@ -63,7 +64,7 @@ export class UserRepository extends Repository { public async pack( src: User['id'] | User, - me?: User['id'] | User, + me?: User['id'] | User | null | undefined, options?: { detail?: boolean, includeSecrets?: boolean, @@ -75,12 +76,12 @@ export class UserRepository extends Repository { includeSecrets: false }, options); - const user = typeof src === 'object' ? src : await this.findOne(src); + const user = typeof src === 'object' ? src : await this.findOne(src).then(ensure); const meId = me ? typeof me === 'string' ? me : me.id : null; const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : []; - const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }) : null; + const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }).then(ensure) : null; return await rap({ id: user.id, @@ -117,12 +118,12 @@ export class UserRepository extends Repository { } : {}), ...(opts.detail ? { - url: profile.url, + url: profile!.url, createdAt: user.createdAt, updatedAt: user.updatedAt, - description: profile.description, - location: profile.location, - birthday: profile.birthday, + description: profile!.description, + location: profile!.location, + birthday: profile!.birthday, followersCount: user.followersCount, followingCount: user.followingCount, notesCount: user.notesCount, @@ -135,9 +136,9 @@ export class UserRepository extends Repository { ...(opts.detail && meId === user.id ? { avatarId: user.avatarId, bannerId: user.bannerId, - autoWatch: profile.autoWatch, - alwaysMarkNsfw: profile.alwaysMarkNsfw, - carefulBot: profile.carefulBot, + autoWatch: profile!.autoWatch, + alwaysMarkNsfw: profile!.alwaysMarkNsfw, + carefulBot: profile!.carefulBot, hasUnreadMessagingMessage: MessagingMessages.count({ where: { recipientId: user.id, @@ -158,9 +159,9 @@ export class UserRepository extends Repository { } : {}), ...(opts.includeSecrets ? { - clientData: profile.clientData, - email: profile.email, - emailVerified: profile.emailVerified, + clientData: profile!.clientData, + email: profile!.email, + emailVerified: profile!.emailVerified, } : {}), ...(relation ? { diff --git a/src/prelude/ensure.ts b/src/prelude/ensure.ts new file mode 100644 index 0000000000..90bf05538a --- /dev/null +++ b/src/prelude/ensure.ts @@ -0,0 +1,7 @@ +export function ensure(x: T): NonNullable { + if (x == null) { + throw 'ぬるぽ'; + } else { + return x!; + } +} diff --git a/src/queue/index.ts b/src/queue/index.ts index 728c43c6ac..1ab59fd18f 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -12,7 +12,7 @@ import { queueLogger } from './logger'; import { DriveFile } from '../models/entities/drive-file'; function initializeQueue(name: string) { - return new Queue(name, config.redis != null ? { + return new Queue(name, { redis: { port: config.redis.port, host: config.redis.host, @@ -20,7 +20,7 @@ function initializeQueue(name: string) { db: config.redis.db || 0, }, prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue' - } : null); + }); } export const deliverQueue = initializeQueue('deliver'); diff --git a/src/queue/processors/db/delete-drive-files.ts b/src/queue/processors/db/delete-drive-files.ts index 5f347fb588..491734acc6 100644 --- a/src/queue/processors/db/delete-drive-files.ts +++ b/src/queue/processors/db/delete-drive-files.ts @@ -10,9 +10,11 @@ const logger = queueLogger.createSubLogger('delete-drive-files'); export async function deleteDriveFiles(job: Bull.Job, done: any): Promise { logger.info(`Deleting drive files of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } let deletedCount = 0; let ended = false; diff --git a/src/queue/processors/db/export-blocking.ts b/src/queue/processors/db/export-blocking.ts index c12aa4fca3..44025ec960 100644 --- a/src/queue/processors/db/export-blocking.ts +++ b/src/queue/processors/db/export-blocking.ts @@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-blocking'); export async function exportBlocking(job: Bull.Job, done: any): Promise { logger.info(`Exporting blocking of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } // Create temp file const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { @@ -56,6 +58,10 @@ export async function exportBlocking(job: Bull.Job, done: any): Promise { for (const block of blockings) { const u = await Users.findOne({ id: block.blockeeId }); + if (u == null) { + exportedCount++; continue; + } + const content = getFullApAccount(u.username, u.host); await new Promise((res, rej) => { stream.write(content + '\n', err => { diff --git a/src/queue/processors/db/export-following.ts b/src/queue/processors/db/export-following.ts index fb30df79fe..81dcf8f93e 100644 --- a/src/queue/processors/db/export-following.ts +++ b/src/queue/processors/db/export-following.ts @@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-following'); export async function exportFollowing(job: Bull.Job, done: any): Promise { logger.info(`Exporting following of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } // Create temp file const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { @@ -56,6 +58,10 @@ export async function exportFollowing(job: Bull.Job, done: any): Promise { for (const following of followings) { const u = await Users.findOne({ id: following.followeeId }); + if (u == null) { + exportedCount++; continue; + } + const content = getFullApAccount(u.username, u.host); await new Promise((res, rej) => { stream.write(content + '\n', err => { diff --git a/src/queue/processors/db/export-mute.ts b/src/queue/processors/db/export-mute.ts index 3aed526dc5..f810b6ee8d 100644 --- a/src/queue/processors/db/export-mute.ts +++ b/src/queue/processors/db/export-mute.ts @@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-mute'); export async function exportMute(job: Bull.Job, done: any): Promise { logger.info(`Exporting mute of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } // Create temp file const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { @@ -56,6 +58,10 @@ export async function exportMute(job: Bull.Job, done: any): Promise { for (const mute of mutes) { const u = await Users.findOne({ id: mute.muteeId }); + if (u == null) { + exportedCount++; continue; + } + const content = getFullApAccount(u.username, u.host); await new Promise((res, rej) => { stream.write(content + '\n', err => { diff --git a/src/queue/processors/db/export-notes.ts b/src/queue/processors/db/export-notes.ts index 92867ad82e..eaa5caf63f 100644 --- a/src/queue/processors/db/export-notes.ts +++ b/src/queue/processors/db/export-notes.ts @@ -9,15 +9,18 @@ import { Users, Notes, Polls } from '../../../models'; import { MoreThan } from 'typeorm'; import { Note } from '../../../models/entities/note'; import { Poll } from '../../../models/entities/poll'; +import { ensure } from '../../../prelude/ensure'; const logger = queueLogger.createSubLogger('export-notes'); export async function exportNotes(job: Bull.Job, done: any): Promise { logger.info(`Exporting notes of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } // Create temp file const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { @@ -67,9 +70,9 @@ export async function exportNotes(job: Bull.Job, done: any): Promise { cursor = notes[notes.length - 1].id; for (const note of notes) { - let poll: Poll; + let poll: Poll | undefined; if (note.hasPoll) { - poll = await Polls.findOne({ noteId: note.id }); + poll = await Polls.findOne({ noteId: note.id }).then(ensure); } const content = JSON.stringify(serialize(note, poll)); await new Promise((res, rej) => { @@ -114,7 +117,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise { done(); } -function serialize(note: Note, poll: Poll): any { +function serialize(note: Note, poll: Poll | null = null): any { return { id: note.id, text: note.text, diff --git a/src/queue/processors/db/export-user-lists.ts b/src/queue/processors/db/export-user-lists.ts index f3987cb0d2..5cd978c1aa 100644 --- a/src/queue/processors/db/export-user-lists.ts +++ b/src/queue/processors/db/export-user-lists.ts @@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-user-lists'); export async function exportUserLists(job: Bull.Job, done: any): Promise { logger.info(`Exporting user lists of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } const lists = await UserLists.find({ userId: user.id diff --git a/src/queue/processors/db/import-following.ts b/src/queue/processors/db/import-following.ts index aae24b22d6..8de3193e46 100644 --- a/src/queue/processors/db/import-following.ts +++ b/src/queue/processors/db/import-following.ts @@ -13,13 +13,19 @@ const logger = queueLogger.createSubLogger('import-following'); export async function importFollowing(job: Bull.Job, done: any): Promise { logger.info(`Importing following of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } const file = await DriveFiles.findOne({ id: job.data.fileId }); + if (file == null) { + done(); + return; + } const csv = await downloadTextFile(file.url); @@ -31,11 +37,11 @@ export async function importFollowing(job: Bull.Job, done: any): Promise { try { const { username, host } = parseAcct(line.trim()); - let target = isSelfHost(host) ? await Users.findOne({ + let target = isSelfHost(host!) ? await Users.findOne({ host: null, usernameLower: username.toLowerCase() }) : await Users.findOne({ - host: toPuny(host), + host: toPuny(host!), usernameLower: username.toLowerCase() }); diff --git a/src/queue/processors/db/import-user-lists.ts b/src/queue/processors/db/import-user-lists.ts index c7273ea6b4..1e852be945 100644 --- a/src/queue/processors/db/import-user-lists.ts +++ b/src/queue/processors/db/import-user-lists.ts @@ -14,13 +14,19 @@ const logger = queueLogger.createSubLogger('import-user-lists'); export async function importUserLists(job: Bull.Job, done: any): Promise { logger.info(`Importing user lists of ${job.data.user.id} ...`); - const user = await Users.findOne({ - id: job.data.user.id - }); + const user = await Users.findOne(job.data.user.id); + if (user == null) { + done(); + return; + } const file = await DriveFiles.findOne({ id: job.data.fileId }); + if (file == null) { + done(); + return; + } const csv = await downloadTextFile(file.url); @@ -43,22 +49,20 @@ export async function importUserLists(job: Bull.Job, done: any): Promise { }); } - let target = isSelfHost(host) ? await Users.findOne({ + let target = isSelfHost(host!) ? await Users.findOne({ host: null, usernameLower: username.toLowerCase() }) : await Users.findOne({ - host: toPuny(host), + host: toPuny(host!), usernameLower: username.toLowerCase() }); - if (host == null && target == null) continue; - - if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue; - if (target == null) { target = await resolveUser(username, host); } + if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue; + pushUserToUserList(target, list); } diff --git a/src/queue/processors/deliver.ts b/src/queue/processors/deliver.ts index b9701c0c65..8837c80d87 100644 --- a/src/queue/processors/deliver.ts +++ b/src/queue/processors/deliver.ts @@ -7,7 +7,7 @@ import { instanceChart } from '../../services/chart'; const logger = new Logger('deliver'); -let latest: string = null; +let latest: string | null = null; export default async (job: Bull.Job) => { const { host } = new URL(job.data.to); diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts index 35b0ce5386..4deaef2ae3 100644 --- a/src/queue/processors/inbox.ts +++ b/src/queue/processors/inbox.ts @@ -14,6 +14,7 @@ import { UserPublickey } from '../../models/entities/user-publickey'; import fetchMeta from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; import { validActor } from '../../remote/activitypub/type'; +import { ensure } from '../../prelude/ensure'; const logger = new Logger('inbox'); @@ -35,7 +36,7 @@ export default async (job: Bull.Job): Promise => { if (keyIdLower.startsWith('acct:')) { const acct = parseAcct(keyIdLower.slice('acct:'.length)); - const host = toPuny(acct.host); + const host = acct.host ? toPuny(acct.host) : null; const username = toPuny(acct.username); if (host === null) { @@ -64,9 +65,7 @@ export default async (job: Bull.Job): Promise => { host: host }) as IRemoteUser; - key = await UserPublickeys.findOne({ - userId: user.id - }); + key = await UserPublickeys.findOne(user.id).then(ensure); } else { // アクティビティ内のホストの検証 const host = toPuny(new URL(signature.keyId).hostname); @@ -87,7 +86,7 @@ export default async (job: Bull.Job): Promise => { key = await UserPublickeys.findOne({ keyId: signature.keyId - }); + }).then(ensure); user = await Users.findOne(key.userId) as IRemoteUser; } diff --git a/src/remote/activitypub/kernel/accept/follow.ts b/src/remote/activitypub/kernel/accept/follow.ts index 816fcbadbf..f3e517ad9f 100644 --- a/src/remote/activitypub/kernel/accept/follow.ts +++ b/src/remote/activitypub/kernel/accept/follow.ts @@ -6,9 +6,10 @@ import { Users } from '../../../../models'; export default async (actor: IRemoteUser, activity: IFollow): Promise => { const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id; + if (id == null) throw 'missing id'; if (!id.startsWith(config.url + '/')) { - return null; + return; } const follower = await Users.findOne({ diff --git a/src/remote/activitypub/kernel/add/index.ts b/src/remote/activitypub/kernel/add/index.ts index d16f0a4a0d..a5b2687416 100644 --- a/src/remote/activitypub/kernel/add/index.ts +++ b/src/remote/activitypub/kernel/add/index.ts @@ -14,6 +14,7 @@ export default async (actor: IRemoteUser, activity: IAdd): Promise => { if (activity.target === actor.featured) { const note = await resolveNote(activity.object); + if (note == null) throw new Error('note not found'); await addPinned(actor, note.id); return; } diff --git a/src/remote/activitypub/kernel/announce/note.ts b/src/remote/activitypub/kernel/announce/note.ts index 403fc66bed..f9822c5187 100644 --- a/src/remote/activitypub/kernel/announce/note.ts +++ b/src/remote/activitypub/kernel/announce/note.ts @@ -53,16 +53,16 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: logger.info(`Creating the (Re)Note: ${uri}`); //#region Visibility - const visibility = getVisibility(activity.to, activity.cc, actor); + const visibility = getVisibility(activity.to || [], activity.cc || [], actor); let visibleUsers: User[] = []; if (visibility == 'specified') { - visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri))); + visibleUsers = await Promise.all((note.to || []).map(uri => resolvePerson(uri))); } //#endergion await post(actor, { - createdAt: new Date(activity.published), + createdAt: activity.published ? new Date(activity.published) : null, renote, visibility, visibleUsers, @@ -75,9 +75,6 @@ type visibility = 'public' | 'home' | 'followers' | 'specified'; function getVisibility(to: string[], cc: string[], actor: IRemoteUser): visibility { const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public'; - to = to || []; - cc = cc || []; - if (to.includes(PUBLIC)) { return 'public'; } else if (cc.includes(PUBLIC)) { diff --git a/src/remote/activitypub/kernel/block/index.ts b/src/remote/activitypub/kernel/block/index.ts index 48e251dd9b..19e33eb7dd 100644 --- a/src/remote/activitypub/kernel/block/index.ts +++ b/src/remote/activitypub/kernel/block/index.ts @@ -9,13 +9,14 @@ const logger = apLogger; export default async (actor: IRemoteUser, activity: IBlock): Promise => { const id = typeof activity.object == 'string' ? activity.object : activity.object.id; + if (id == null) throw 'missing id'; const uri = activity.id || activity; logger.info(`Block: ${uri}`); if (!id.startsWith(config.url + '/')) { - return null; + return; } const blockee = await Users.findOne(id.split('/').pop()); diff --git a/src/remote/activitypub/kernel/follow.ts b/src/remote/activitypub/kernel/follow.ts index e6c8833f3a..d37404502f 100644 --- a/src/remote/activitypub/kernel/follow.ts +++ b/src/remote/activitypub/kernel/follow.ts @@ -6,9 +6,10 @@ import { Users } from '../../../models'; export default async (actor: IRemoteUser, activity: IFollow): Promise => { const id = typeof activity.object == 'string' ? activity.object : activity.object.id; + if (id == null) throw 'missing id'; if (!id.startsWith(config.url + '/')) { - return null; + return; } const followee = await Users.findOne(id.split('/').pop()); diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts index 4a57d0675e..d1251817fa 100644 --- a/src/remote/activitypub/kernel/index.ts +++ b/src/remote/activitypub/kernel/index.ts @@ -71,7 +71,7 @@ const self = async (actor: IRemoteUser, activity: Object): Promise => { default: apLogger.warn(`unknown activity type: ${(activity as any).type}`); - return null; + return; } }; diff --git a/src/remote/activitypub/kernel/like.ts b/src/remote/activitypub/kernel/like.ts index 86dd8fb33d..d4fa7bf387 100644 --- a/src/remote/activitypub/kernel/like.ts +++ b/src/remote/activitypub/kernel/like.ts @@ -5,6 +5,7 @@ import { Notes } from '../../../models'; export default async (actor: IRemoteUser, activity: ILike) => { const id = typeof activity.object == 'string' ? activity.object : activity.object.id; + if (id == null) throw 'missing id'; // Transform: // https://misskey.ex/notes/xxxx to diff --git a/src/remote/activitypub/kernel/reject/follow.ts b/src/remote/activitypub/kernel/reject/follow.ts index b06ae6fb96..91689339ab 100644 --- a/src/remote/activitypub/kernel/reject/follow.ts +++ b/src/remote/activitypub/kernel/reject/follow.ts @@ -6,9 +6,10 @@ import { Users } from '../../../../models'; export default async (actor: IRemoteUser, activity: IFollow): Promise => { const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id; + if (id == null) throw 'missing id'; if (!id.startsWith(config.url + '/')) { - return null; + return; } const follower = await Users.findOne(id.split('/').pop()); diff --git a/src/remote/activitypub/kernel/remove/index.ts b/src/remote/activitypub/kernel/remove/index.ts index ae33be59dc..32b8d66471 100644 --- a/src/remote/activitypub/kernel/remove/index.ts +++ b/src/remote/activitypub/kernel/remove/index.ts @@ -14,6 +14,7 @@ export default async (actor: IRemoteUser, activity: IRemove): Promise => { if (activity.target === actor.featured) { const note = await resolveNote(activity.object); + if (note == null) throw new Error('note not found'); await removePinned(actor, note.id); return; } diff --git a/src/remote/activitypub/kernel/undo/block.ts b/src/remote/activitypub/kernel/undo/block.ts index c916a00737..9c277ed7d2 100644 --- a/src/remote/activitypub/kernel/undo/block.ts +++ b/src/remote/activitypub/kernel/undo/block.ts @@ -9,13 +9,14 @@ const logger = apLogger; export default async (actor: IRemoteUser, activity: IBlock): Promise => { const id = typeof activity.object == 'string' ? activity.object : activity.object.id; + if (id == null) throw 'missing id'; const uri = activity.id || activity; logger.info(`UnBlock: ${uri}`); if (!id.startsWith(config.url + '/')) { - return null; + return; } const blockee = await Users.findOne(id.split('/').pop()); diff --git a/src/remote/activitypub/kernel/undo/follow.ts b/src/remote/activitypub/kernel/undo/follow.ts index cc63a740b1..ce84d0c791 100644 --- a/src/remote/activitypub/kernel/undo/follow.ts +++ b/src/remote/activitypub/kernel/undo/follow.ts @@ -7,9 +7,10 @@ import { Users, FollowRequests, Followings } from '../../../../models'; export default async (actor: IRemoteUser, activity: IFollow): Promise => { const id = typeof activity.object == 'string' ? activity.object : activity.object.id; + if (id == null) throw 'missing id'; if (!id.startsWith(config.url + '/')) { - return null; + return; } const followee = await Users.findOne(id.split('/').pop()); diff --git a/src/remote/activitypub/kernel/undo/index.ts b/src/remote/activitypub/kernel/undo/index.ts index 6376ab93a8..5f2e58c3bf 100644 --- a/src/remote/activitypub/kernel/undo/index.ts +++ b/src/remote/activitypub/kernel/undo/index.ts @@ -39,6 +39,4 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise => { undoLike(actor, object as ILike); break; } - - return null; }; diff --git a/src/remote/activitypub/kernel/undo/like.ts b/src/remote/activitypub/kernel/undo/like.ts index f337a0173e..75879d697a 100644 --- a/src/remote/activitypub/kernel/undo/like.ts +++ b/src/remote/activitypub/kernel/undo/like.ts @@ -8,6 +8,7 @@ import { Notes } from '../../../../models'; */ export default async (actor: IRemoteUser, activity: ILike): Promise => { const id = typeof activity.object == 'string' ? activity.object : activity.object.id; + if (id == null) throw 'missing id'; const noteId = id.split('/').pop(); diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts index c9991dba3b..f8b35ea21c 100644 --- a/src/remote/activitypub/models/image.ts +++ b/src/remote/activitypub/models/image.ts @@ -5,6 +5,7 @@ import fetchMeta from '../../../misc/fetch-meta'; import { apLogger } from '../logger'; import { DriveFile } from '../../../models/entities/drive-file'; import { DriveFiles } from '../../../models'; +import { ensure } from '../../../prelude/ensure'; const logger = apLogger; @@ -14,7 +15,7 @@ const logger = apLogger; export async function createImage(actor: IRemoteUser, value: any): Promise { // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { - return null; + throw new Error('actor has been suspended'); } const image = await new Resolver().resolve(value) as any; @@ -28,17 +29,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise= 400 && e < 500) { - logger.warn(`Ignored image: ${image.url} - ${e}`); - return null; - } - throw e; - } + let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache); if (file.isLink) { // URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、 @@ -49,7 +40,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise { +export async function fetchNote(value: string | IObject, resolver?: Resolver): Promise { const uri = typeof value == 'string' ? value : value.id; + if (uri == null) throw 'missing uri'; // URIがこのサーバーを指しているならデータベースからフェッチ if (uri.startsWith(config.url + '/')) { const id = uri.split('/').pop(); - return await Notes.findOne(id); + return await Notes.findOne(id).then(x => x || null); } //#region このサーバーに既に登録されていたらそれを返す @@ -52,7 +54,7 @@ export async function fetchNote(value: string | IObject, resolver?: Resolver): P /** * Noteを作成します。 */ -export async function createNote(value: any, resolver?: Resolver, silent = false): Promise { +export async function createNote(value: any, resolver?: Resolver, silent = false): Promise { if (resolver == null) resolver = new Resolver(); const object: any = await resolver.resolve(value); @@ -65,7 +67,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false value: value, object: object }); - return null; + throw 'invalid note'; } const note: INote = object; @@ -75,11 +77,11 @@ export async function createNote(value: any, resolver?: Resolver, silent = false logger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッチ - const actor = await resolvePerson(note.attributedTo, null, resolver) as IRemoteUser; + const actor = await resolvePerson(note.attributedTo, resolver) as IRemoteUser; // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { - return null; + throw 'actor has been suspended'; } //#region Visibility @@ -95,9 +97,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false visibility = 'followers'; } else { visibility = 'specified'; - visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, null, resolver))); + visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, resolver))); } -} + } //#endergion const apMentions = await extractMentionedUsers(actor, note.to, note.cc, resolver); @@ -118,7 +120,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false : []; // リプライ - const reply: Note = note.inReplyTo + const reply: Note | undefined | null = note.inReplyTo ? await resolveNote(note.inReplyTo, resolver).catch(e => { // 4xxの場合はリプライしてないことにする if (e.statusCode >= 400 && e.statusCode < 500) { @@ -131,7 +133,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false : null; // 引用 - let quote: Note; + let quote: Note | undefined | null; if (note._misskey_quote && typeof note._misskey_quote == 'string') { quote = await resolveNote(note._misskey_quote).catch(e => { @@ -152,7 +154,8 @@ export async function createNote(value: any, resolver?: Resolver, silent = false // vote if (reply && reply.hasPoll) { - const poll = await Polls.findOne({ noteId: reply.id }); + const poll = await Polls.findOne({ noteId: reply.id }).then(ensure); + const tryCreateVote = async (name: string, index: number): Promise => { if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); @@ -180,7 +183,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false } } - const emojis = await extractEmojis(note.tag, actor.host).catch(e => { + const emojis = await extractEmojis(note.tag || [], actor.host).catch(e => { logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); @@ -196,7 +199,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false } return await post(actor, { - createdAt: new Date(note.published), + createdAt: note.published ? new Date(note.published) : null, files, reply, renote: quote, @@ -223,8 +226,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ -export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise { +export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise { const uri = typeof value == 'string' ? value : value.id; + if (uri == null) throw 'missing uri'; // ブロックしてたら中断 // TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく @@ -244,75 +248,79 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver): // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 return await createNote(uri, resolver).catch(e => { if (e.name === 'duplicated') { - return fetchNote(uri); + return fetchNote(uri).then(note => { + if (note == null) { + throw 'something happened'; + } else { + return note; + } + }); } else { throw e; } }); } -export async function extractEmojis(tags: ITag[], host: string) { +export async function extractEmojis(tags: ITag[], host: string): Promise { host = toPuny(host); if (!tags) return []; - const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url); - - return await Promise.all( - eomjiTags.map(async tag => { - const name = tag.name.replace(/^:/, '').replace(/:$/, ''); + const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url && tag.name); - const exists = await Emojis.findOne({ - host, - name - }); + return await Promise.all(eomjiTags.map(async tag => { + const name = tag.name!.replace(/^:/, '').replace(/:$/, ''); - if (exists) { - if ((tag.updated != null && exists.updatedAt == null) - || (tag.id != null && exists.uri == null) - || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) - ) { - await Emojis.update({ - host, - name, - }, { - uri: tag.id, - url: tag.icon.url, - updatedAt: new Date(tag.updated), - }); - - return await Emojis.findOne({ - host, - name - }); - } + const exists = await Emojis.findOne({ + host, + name + }); - return exists; + if (exists) { + if ((tag.updated != null && exists.updatedAt == null) + || (tag.id != null && exists.uri == null) + || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) + ) { + await Emojis.update({ + host, + name, + }, { + uri: tag.id, + url: tag.icon!.url, + updatedAt: new Date(tag.updated!), + }); + + return await Emojis.findOne({ + host, + name + }) as Emoji; } - logger.info(`register emoji host=${host}, name=${name}`); - - return await Emojis.save({ - id: genId(), - host, - name, - uri: tag.id, - url: tag.icon.url, - updatedAt: tag.updated ? new Date(tag.updated) : undefined, - aliases: [] - } as Emoji); - }) - ); + return exists; + } + + logger.info(`register emoji host=${host}, name=${name}`); + + return await Emojis.save({ + id: genId(), + host, + name, + uri: tag.id, + url: tag.icon!.url, + updatedAt: tag.updated ? new Date(tag.updated) : undefined, + aliases: [] + } as Partial); + })); } async function extractMentionedUsers(actor: IRemoteUser, to: string[], cc: string[], resolver: Resolver) { const ignoreUris = ['https://www.w3.org/ns/activitystreams#Public', `${actor.uri}/followers`]; const uris = difference(unique(concat([to || [], cc || []])), ignoreUris); - const limit = promiseLimit(2); + const limit = promiseLimit(2); const users = await Promise.all( - uris.map(uri => limit(() => resolvePerson(uri, null, resolver).catch(() => null)) as Promise) + uris.map(uri => limit(() => resolvePerson(uri, resolver).catch(() => null)) as Promise) ); - return users.filter(x => x != null); + return users.filter(x => x != null) as User[]; } diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index e7021956de..9465cf0cd0 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -26,6 +26,7 @@ import { toPuny } from '../../../misc/convert-host'; import { UserProfile } from '../../../models/entities/user-profile'; import { validActor } from '../../../remote/activitypub/type'; import { getConnection } from 'typeorm'; +import { ensure } from '../../../prelude/ensure'; const logger = apLogger; /** @@ -86,13 +87,13 @@ function validatePerson(x: any, uri: string) { * * Misskeyに対象のPersonが登録されていればそれを返します。 */ -export async function fetchPerson(uri: string, resolver?: Resolver): Promise { +export async function fetchPerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw 'uri is not string'; // URIがこのサーバーを指しているならデータベースからフェッチ if (uri.startsWith(config.url + '/')) { const id = uri.split('/').pop(); - return await Users.findOne(id); + return await Users.findOne(id).then(x => x || null); } //#region このサーバーに既に登録されていたらそれを返す @@ -128,7 +129,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise tag.toLowerCase()); @@ -161,7 +162,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise !tags.includes(x))) updateHashtag(user, tag, true, false); + for (const tag of tags) updateHashtag(user!, tag, true, true); + for (const tag of (user!.tags || []).filter(x => !tags.includes(x))) updateHashtag(user!, tag, true, false); //#region アイコンとヘッダー画像をフェッチ - const [avatar, banner] = (await Promise.all([ + const [avatar, banner] = (await Promise.all([ person.icon, person.image ].map(img => img == null ? Promise.resolve(null) - : resolveImage(user, img).catch(() => null) + : resolveImage(user!, img).catch(() => null) ))); const avatarId = avatar ? avatar.id : null; @@ -210,9 +211,9 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { + const emojis = await extractEmojis(person.tag || [], host).catch(e => { logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); const emojiNames = emojis.map(emoji => emoji.name); - await Users.update(user.id, { + await Users.update(user!.id, { emojis: emojiNames }); //#endregion - await updateFeatured(user.id).catch(err => logger.error(err)); + await updateFeatured(user!.id).catch(err => logger.error(err)); - return user; + return user!; } /** @@ -254,7 +255,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { +export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: object): Promise { if (typeof uri !== 'string') throw 'uri is not string'; // URIがこのサーバーを指しているならスキップ @@ -290,7 +291,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje logger.info(`Updating the Person: ${person.id}`); // アイコンとヘッダー画像をフェッチ - const [avatar, banner] = (await Promise.all([ + const [avatar, banner] = (await Promise.all([ person.icon, person.image ].map(img => @@ -300,14 +301,14 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje ))); // カスタム絵文字取得 - const emojis = await extractEmojis(person.tag, exist.host).catch(e => { + const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => { logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); const emojiNames = emojis.map(emoji => emoji.name); - const { fields, services } = analyzeAttachments(person.attachment); + const { fields, services } = analyzeAttachments(person.attachment || []); const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()); @@ -317,7 +318,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), featured: person.featured, emojis: emojiNames, - description: fromHtml(person.summary), + description: person.summary ? fromHtml(person.summary) : null, name: person.name, url: person.url, endpoints: person.endpoints, @@ -326,7 +327,6 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje isBot: object.type == 'Service', isCat: (person as any).isCat === true, isLocked: person.manuallyApprovesFollowers, - createdAt: new Date(Date.parse(person.published)) || null, } as Partial; if (avatar) { @@ -379,7 +379,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ -export async function resolvePerson(uri: string, verifier?: string, resolver?: Resolver): Promise { +export async function resolvePerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw 'uri is not string'; //#region このサーバーに既に登録されていたらそれを返す @@ -439,21 +439,24 @@ export function analyzeAttachments(attachments: ITag[]) { }[] = []; const services: { [x: string]: any } = {}; - if (Array.isArray(attachments)) - for (const attachment of attachments.filter(isPropertyValue)) - if (isPropertyValue(attachment.identifier)) - addService(services, attachment.identifier); - else + if (Array.isArray(attachments)) { + for (const attachment of attachments.filter(isPropertyValue)) { + if (isPropertyValue(attachment.identifier!)) { + addService(services, attachment.identifier!); + } else { fields.push({ - name: attachment.name, - value: fromHtml(attachment.value) + name: attachment.name!, + value: fromHtml(attachment.value!) }); + } + } + } return { fields, services }; } export async function updateFeatured(userId: User['id']) { - const user = await Users.findOne(userId); + const user = await Users.findOne(userId).then(ensure); if (!Users.isRemoteUser(user)) return; if (!user.featured) return; @@ -471,18 +474,18 @@ export async function updateFeatured(userId: User['id']) { if (!Array.isArray(items)) throw new Error(`Collection items is not an array`); // Resolve and regist Notes - const limit = promiseLimit(2); + const limit = promiseLimit(2); const featuredNotes = await Promise.all(items .filter(item => item.type === 'Note') .slice(0, 5) - .map(item => limit(() => resolveNote(item, resolver)) as Promise)); + .map(item => limit(() => resolveNote(item, resolver)))); for (const note of featuredNotes.filter(note => note != null)) { UserNotePinings.save({ id: genId(), createdAt: new Date(), userId: user.id, - noteId: note.id + noteId: note!.id } as UserNotePining); } } diff --git a/src/remote/activitypub/models/question.ts b/src/remote/activitypub/models/question.ts index 2ff8e21ab5..708cdc2a66 100644 --- a/src/remote/activitypub/models/question.ts +++ b/src/remote/activitypub/models/question.ts @@ -14,10 +14,10 @@ export async function extractPollFromQuestion(source: string | IQuestion): Promi throw 'invalid question'; } - const choices = question[multiple ? 'anyOf' : 'oneOf'] - .map((x, i) => x.name); + const choices = question[multiple ? 'anyOf' : 'oneOf']! + .map((x, i) => x.name!); - const votes = question[multiple ? 'anyOf' : 'oneOf'] + const votes = question[multiple ? 'anyOf' : 'oneOf']! .map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0); return { @@ -60,7 +60,7 @@ export async function updateQuestion(value: any) { for (const choice of poll.choices) { const oldCount = poll.votes[poll.choices.indexOf(choice)]; - const newCount = apChoices.filter(ap => ap.name === choice)[0].replies.totalItems; + const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems; if (oldCount != newCount) { changed = true; diff --git a/src/remote/activitypub/models/tag.ts b/src/remote/activitypub/models/tag.ts index 0a1e6e29f9..8d2008d1d9 100644 --- a/src/remote/activitypub/models/tag.ts +++ b/src/remote/activitypub/models/tag.ts @@ -14,13 +14,13 @@ export type ITag = { identifier?: IIdentifier; }; -export function extractHashtags(tags: ITag[]) { - if (!tags) return []; +export function extractHashtags(tags: ITag[] | null | undefined): string[] { + if (tags == null) return []; const hashtags = tags.filter(tag => tag.type === 'Hashtag' && typeof tag.name == 'string'); return hashtags.map(tag => { - const m = tag.name.match(/^#(.+)/); + const m = tag.name ? tag.name.match(/^#(.+)/) : null; return m ? m[1] : null; - }).filter(x => x != null); + }).filter(x => x != null) as string[]; } diff --git a/src/remote/activitypub/renderer/block.ts b/src/remote/activitypub/renderer/block.ts index 946c45a813..c29a9aea82 100644 --- a/src/remote/activitypub/renderer/block.ts +++ b/src/remote/activitypub/renderer/block.ts @@ -1,7 +1,7 @@ import config from '../../../config'; import { ILocalUser, IRemoteUser } from '../../../models/entities/user'; -export default (blocker?: ILocalUser, blockee?: IRemoteUser) => ({ +export default (blocker: ILocalUser, blockee: IRemoteUser) => ({ type: 'Block', actor: `${config.url}/users/${blocker.id}`, object: blockee.uri diff --git a/src/remote/activitypub/renderer/follow-user.ts b/src/remote/activitypub/renderer/follow-user.ts index 9446be3c86..6d354803e5 100644 --- a/src/remote/activitypub/renderer/follow-user.ts +++ b/src/remote/activitypub/renderer/follow-user.ts @@ -1,12 +1,13 @@ import config from '../../../config'; import { Users } from '../../../models'; import { User } from '../../../models/entities/user'; +import { ensure } from '../../../prelude/ensure'; /** * Convert (local|remote)(Follower|Followee)ID to URL * @param id Follower|Followee ID */ export default async function renderFollowUser(id: User['id']): Promise { - const user = await Users.findOne(id); + const user = await Users.findOne(id).then(ensure); return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri; } diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts index 58ee4fb52c..c66af2667b 100644 --- a/src/remote/activitypub/renderer/note.ts +++ b/src/remote/activitypub/renderer/note.ts @@ -10,6 +10,7 @@ import { DriveFiles, Notes, Users, Emojis, Polls } from '../../../models'; import { In } from 'typeorm'; import { Emoji } from '../../../models/entities/emoji'; import { Poll } from '../../../models/entities/poll'; +import { ensure } from '../../../prelude/ensure'; export default async function renderNote(note: Note, dive = true): Promise { const promisedFiles: Promise = note.fileIds.length > 0 @@ -17,15 +18,15 @@ export default async function renderNote(note: Note, dive = true): Promise : Promise.resolve([]); let inReplyTo; - let inReplyToNote: Note; + let inReplyToNote: Note | undefined; if (note.replyId) { inReplyToNote = await Notes.findOne(note.replyId); - if (inReplyToNote !== null) { + if (inReplyToNote != null) { const inReplyToUser = await Users.findOne(inReplyToNote.userId); - if (inReplyToUser !== null) { + if (inReplyToUser != null) { if (inReplyToNote.uri) { inReplyTo = inReplyToNote.uri; } else { @@ -51,9 +52,7 @@ export default async function renderNote(note: Note, dive = true): Promise } } - const user = await Users.findOne({ - id: note.userId - }); + const user = await Users.findOne(note.userId).then(ensure); const attributedTo = `${config.url}/users/${user.id}`; @@ -85,13 +84,13 @@ export default async function renderNote(note: Note, dive = true): Promise const files = await promisedFiles; let text = note.text; - let poll: Poll; + let poll: Poll | undefined; if (note.hasPoll) { poll = await Polls.findOne({ noteId: note.id }); } - let question: string; + let question: string | undefined; if (poll) { if (text == null) text = ''; const url = `${config.url}/notes/${note.id}`; @@ -144,7 +143,7 @@ export default async function renderNote(note: Note, dive = true): Promise name: text, replies: { type: 'Collection', - totalItems: poll.votes[i] + totalItems: poll!.votes[i] } })) } : {}; @@ -179,5 +178,5 @@ export async function getEmojis(names: string[]): Promise { })) ); - return emojis.filter(emoji => emoji != null); + return emojis.filter(emoji => emoji != null) as Emoji[]; } diff --git a/src/remote/activitypub/renderer/ordered-collection-page.ts b/src/remote/activitypub/renderer/ordered-collection-page.ts index 83af07870e..2433358646 100644 --- a/src/remote/activitypub/renderer/ordered-collection-page.ts +++ b/src/remote/activitypub/renderer/ordered-collection-page.ts @@ -7,7 +7,7 @@ * @param prev URL of prev page (optional) * @param next URL of next page (optional) */ -export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev: string, next: string) { +export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { const page = { id, partOf, diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index e561e47c68..3fb164ef4e 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -9,14 +9,15 @@ import renderEmoji from './emoji'; import { IIdentifier } from '../models/identifier'; import renderHashtag from './hashtag'; import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; +import { ensure } from '../../../prelude/ensure'; export async function renderPerson(user: ILocalUser) { const id = `${config.url}/users/${user.id}`; const [avatar, banner, profile] = await Promise.all([ - DriveFiles.findOne(user.avatarId), - DriveFiles.findOne(user.bannerId), - UserProfiles.findOne({ userId: user.id }) + user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined), + user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined), + UserProfiles.findOne({ userId: user.id }).then(ensure) ]); const attachment: { @@ -76,9 +77,7 @@ export async function renderPerson(user: ILocalUser) { ...hashtagTags, ]; - const keypair = await UserKeypairs.findOne({ - userId: user.id - }); + const keypair = await UserKeypairs.findOne(user.id).then(ensure); return { type: user.isBot ? 'Service' : 'Person', @@ -94,8 +93,8 @@ export async function renderPerson(user: ILocalUser) { preferredUsername: user.username, name: user.name, summary: toHtml(parse(profile.description)), - icon: user.avatarId && renderImage(avatar), - image: user.bannerId && renderImage(banner), + icon: avatar ? renderImage(avatar) : null, + image: banner ? renderImage(banner) : null, tag, manuallyApprovesFollowers: user.isLocked, publicKey: renderKey(user, keypair), diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index 8aca5e8102..7dc48c15e3 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -12,6 +12,7 @@ import { apLogger } from './logger'; import { UserKeypairs } from '../../models'; import fetchMeta from '../../misc/fetch-meta'; import { toPuny } from '../../misc/convert-host'; +import { ensure } from '../../prelude/ensure'; export const logger = apLogger.createSubLogger('deliver'); @@ -38,7 +39,7 @@ export default async (user: ILocalUser, url: string, object: any) => { const keypair = await UserKeypairs.findOne({ userId: user.id - }); + }).then(ensure); const _ = new Promise((resolve, reject) => { const req = request({ @@ -56,7 +57,7 @@ export default async (user: ILocalUser, url: string, object: any) => { 'Digest': `SHA-256=${hash}` } }, res => { - if (res.statusCode >= 400) { + if (res.statusCode! >= 400) { logger.warn(`${url} --> ${res.statusCode}`); reject(res); } else { @@ -73,7 +74,7 @@ export default async (user: ILocalUser, url: string, object: any) => { }); // Signature: Signature ... => Signature: ... - let sig = req.getHeader('Signature').toString(); + let sig = req.getHeader('Signature')!.toString(); sig = sig.replace(/^Signature /, ''); req.setHeader('Signature', sig); @@ -112,7 +113,7 @@ async function resolveAddr(domain: string) { function resolveAddrInner(domain: string, options: IRunOptions = {}): Promise { return new Promise((res, rej) => { - lookup(domain, options, (error: any, address: string | string[]) => { + lookup(domain, options, (error, address) => { if (error) return rej(error); return res(Array.isArray(address) ? address[0] : address); }); diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts index 6a8ce45c91..9b518f5e81 100644 --- a/src/remote/resolve-user.ts +++ b/src/remote/resolve-user.ts @@ -10,18 +10,31 @@ import { toPuny } from '../misc/convert-host'; const logger = remoteLogger.createSubLogger('resolve-user'); -export async function resolveUser(username: string, host: string, option?: any, resync = false): Promise { +export async function resolveUser(username: string, host: string | null, option?: any, resync = false): Promise { const usernameLower = username.toLowerCase(); - host = toPuny(host); if (host == null) { logger.info(`return local user: ${usernameLower}`); - return await Users.findOne({ usernameLower, host: null }); + return await Users.findOne({ usernameLower, host: null }).then(u => { + if (u == null) { + throw 'user not found'; + } else { + return u; + } + }); } + host = toPuny(host); + if (config.host == host) { logger.info(`return local user: ${usernameLower}`); - return await Users.findOne({ usernameLower, host: null }); + return await Users.findOne({ usernameLower, host: null }).then(u => { + if (u == null) { + throw 'user not found'; + } else { + return u; + } + }); } const user = await Users.findOne({ usernameLower, host }, option); @@ -63,7 +76,13 @@ export async function resolveUser(username: string, host: string, option?: any, await updatePerson(self.href); logger.info(`return resynced remote user: ${acctLower}`); - return await Users.findOne({ uri: self.href }); + return await Users.findOne({ uri: self.href }).then(u => { + if (u == null) { + throw 'user not found'; + } else { + return u; + } + }); } logger.info(`return existing remote user: ${acctLower}`); @@ -76,7 +95,7 @@ async function resolveSelf(acctLower: string) { logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); }); - const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self'); + const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); if (!self) { logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); throw new Error('self link not found'); diff --git a/src/remote/webfinger.ts b/src/remote/webfinger.ts index 67535e37db..800673943b 100644 --- a/src/remote/webfinger.ts +++ b/src/remote/webfinger.ts @@ -5,7 +5,7 @@ import { query as urlQuery } from '../prelude/url'; type ILink = { href: string; - rel: string; + rel?: string; }; type IWebFinger = { diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts index 3b39977d47..12fccbfa7d 100644 --- a/src/server/activitypub.ts +++ b/src/server/activitypub.ts @@ -17,6 +17,7 @@ import { isSelfHost } from '../misc/convert-host'; import { Notes, Users, Emojis, UserKeypairs, Polls } from '../models'; import { ILocalUser, User } from '../models/entities/user'; import { In } from 'typeorm'; +import { ensure } from '../prelude/ensure'; // Init router const router = new Router(); @@ -123,8 +124,8 @@ router.get('/questions/:question', async (ctx, next) => { return; } - const user = await Users.findOne(pollNote.userId); - const poll = await Polls.findOne({ noteId: pollNote.id }); + const user = await Users.findOne(pollNote.userId).then(ensure); + const poll = await Polls.findOne({ noteId: pollNote.id }).then(ensure); ctx.body = renderActivity(await renderQuestion(user as ILocalUser, pollNote, poll)); setResponseType(ctx); @@ -156,9 +157,7 @@ router.get('/users/:user/publickey', async ctx => { return; } - const keypair = await UserKeypairs.findOne({ - userId: user.id - }); + const keypair = await UserKeypairs.findOne(user.id).then(ensure); if (Users.isLocalUser(user)) { ctx.body = renderActivity(renderKey(user, keypair)); @@ -189,7 +188,7 @@ router.get('/users/:user', async (ctx, next) => { const user = await Users.findOne({ id: userId, host: null - }); + }).then(ensure); await userInfo(ctx, user); }); @@ -200,7 +199,7 @@ router.get('/@:user', async (ctx, next) => { const user = await Users.findOne({ usernameLower: ctx.params.user.toLowerCase(), host: null - }); + }).then(ensure); await userInfo(ctx, user); }); diff --git a/src/server/activitypub/featured.ts b/src/server/activitypub/featured.ts index f43312d79a..86ec1000c7 100644 --- a/src/server/activitypub/featured.ts +++ b/src/server/activitypub/featured.ts @@ -5,6 +5,7 @@ import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-c import { setResponseType } from '../activitypub'; import renderNote from '../../remote/activitypub/renderer/note'; import { Users, Notes, UserNotePinings } from '../../models'; +import { ensure } from '../../prelude/ensure'; export default async (ctx: Router.IRouterContext) => { const userId = ctx.params.user; @@ -22,13 +23,14 @@ export default async (ctx: Router.IRouterContext) => { const pinings = await UserNotePinings.find({ userId: user.id }); - const pinnedNotes = await Promise.all(pinings.map(pining => Notes.findOne(pining.noteId))); + const pinnedNotes = await Promise.all(pinings.map(pining => + Notes.findOne(pining.noteId).then(ensure))); const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note))); const rendered = renderOrderedCollection( `${config.url}/users/${userId}/collections/featured`, - renderedNotes.length, null, null, renderedNotes + renderedNotes.length, undefined, undefined, renderedNotes ); ctx.body = renderActivity(rendered); diff --git a/src/server/activitypub/followers.ts b/src/server/activitypub/followers.ts index 62c54399ed..e48dc57f7a 100644 --- a/src/server/activitypub/followers.ts +++ b/src/server/activitypub/followers.ts @@ -69,18 +69,18 @@ export default async (ctx: Router.IRouterContext) => { cursor })}`, user.followersCount, renderedFollowers, partOf, - null, + undefined, inStock ? `${partOf}?${url.query({ page: 'true', cursor: followings[followings.length - 1].id - })}` : null + })}` : undefined ); ctx.body = renderActivity(rendered); setResponseType(ctx); } else { // index page - const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`, null); + const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`); ctx.body = renderActivity(rendered); ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); setResponseType(ctx); diff --git a/src/server/activitypub/following.ts b/src/server/activitypub/following.ts index 4894aac1f8..4a7314e0ce 100644 --- a/src/server/activitypub/following.ts +++ b/src/server/activitypub/following.ts @@ -70,18 +70,18 @@ export default async (ctx: Router.IRouterContext) => { cursor })}`, user.followingCount, renderedFollowees, partOf, - null, + undefined, inStock ? `${partOf}?${url.query({ page: 'true', cursor: followings[followings.length - 1].id - })}` : null + })}` : undefined ); ctx.body = renderActivity(rendered); setResponseType(ctx); } else { // index page - const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`, null); + const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`); ctx.body = renderActivity(rendered); ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); setResponseType(ctx); diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts index 377f43c986..118d8f00a9 100644 --- a/src/server/activitypub/outbox.ts +++ b/src/server/activitypub/outbox.ts @@ -15,6 +15,7 @@ import { Users, Notes } from '../../models'; import { makePaginationQuery } from '../api/common/make-pagination-query'; import { Brackets } from 'typeorm'; import { Note } from '../../models/entities/note'; +import { ensure } from '../../prelude/ensure'; export default async (ctx: Router.IRouterContext) => { const userId = ctx.params.user; @@ -73,11 +74,11 @@ export default async (ctx: Router.IRouterContext) => { notes.length ? `${partOf}?${url.query({ page: 'true', since_id: notes[0].id - })}` : null, + })}` : undefined, notes.length ? `${partOf}?${url.query({ page: 'true', until_id: notes[notes.length - 1].id - })}` : null + })}` : undefined ); ctx.body = renderActivity(rendered); @@ -99,9 +100,9 @@ export default async (ctx: Router.IRouterContext) => { * Pack Create or Announce Activity * @param note Note */ -export async function packActivity(note: Note): Promise { +export async function packActivity(note: Note): Promise { if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length == 0)) { - const renote = await Notes.findOne(note.renoteId); + const renote = await Notes.findOne(note.renoteId).then(ensure); return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note); } diff --git a/src/server/api/api-handler.ts b/src/server/api/api-handler.ts index 827aecdf25..2de6994f32 100644 --- a/src/server/api/api-handler.ts +++ b/src/server/api/api-handler.ts @@ -15,11 +15,11 @@ export default (endpoint: IEndpoint, ctx: Koa.BaseContext) => new Promise((res) ctx.status = x; ctx.body = { error: { - message: y.message, - code: y.code, - id: y.id, - kind: y.kind, - ...(y.info ? { info: y.info } : {}) + message: y!.message, + code: y!.code, + id: y!.id, + kind: y!.kind, + ...(y!.info ? { info: y!.info } : {}) } }; } else { @@ -31,9 +31,9 @@ export default (endpoint: IEndpoint, ctx: Koa.BaseContext) => new Promise((res) // Authentication authenticate(body['i']).then(([user, app]) => { // API invoking - call(endpoint.name, user, app, body, (ctx.req as any).file).then(res => { + call(endpoint.name, user, app, body, (ctx.req as any).file).then((res: any) => { reply(res); - }).catch(e => { + }).catch((e: ApiError) => { reply(e.httpStatusCode ? e.httpStatusCode : e.kind == 'client' ? 400 : 500, e); }); }).catch(() => { diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts index e293e3fed0..5c2b7e45cf 100644 --- a/src/server/api/authenticate.ts +++ b/src/server/api/authenticate.ts @@ -3,7 +3,7 @@ import { User } from '../../models/entities/user'; import { App } from '../../models/entities/app'; import { Users, AccessTokens, Apps } from '../../models'; -export default async (token: string): Promise<[User, App]> => { +export default async (token: string): Promise<[User | null | undefined, App | null | undefined]> => { if (token == null) { return [null, null]; } diff --git a/src/server/api/call.ts b/src/server/api/call.ts index d72f2cc9e6..c79f8eef5d 100644 --- a/src/server/api/call.ts +++ b/src/server/api/call.ts @@ -12,7 +12,7 @@ const accessDenied = { id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e' }; -export default async (endpoint: string, user: User, app: App, data: any, file?: any) => { +export default async (endpoint: string, user: User | null | undefined, app: App | null | undefined, data: any, file?: any) => { const isSecure = user != null && app == null; const ep = endpoints.find(e => e.name === endpoint); @@ -39,15 +39,15 @@ export default async (endpoint: string, user: User, app: App, data: any, file?: }); } - if (ep.meta.requireCredential && user.isSuspended) { + if (ep.meta.requireCredential && user!.isSuspended) { throw new ApiError(accessDenied, { reason: 'Your account has been suspended.' }); } - if (ep.meta.requireAdmin && !user.isAdmin) { + if (ep.meta.requireAdmin && !user!.isAdmin) { throw new ApiError(accessDenied, { reason: 'You are not the admin.' }); } - if (ep.meta.requireModerator && !user.isAdmin && !user.isModerator) { + if (ep.meta.requireModerator && !user!.isAdmin && !user!.isModerator) { throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); } @@ -61,7 +61,7 @@ export default async (endpoint: string, user: User, app: App, data: any, file?: if (ep.meta.requireCredential && ep.meta.limit) { // Rate limit - await limiter(ep, user).catch(e => { + await limiter(ep, user!).catch(e => { throw new ApiError({ message: 'Rate limit exceeded. Please try again later.', code: 'RATE_LIMIT_EXCEEDED', diff --git a/src/server/api/common/make-pagination-query.ts b/src/server/api/common/make-pagination-query.ts index 0c859a4f8d..51c11e5dff 100644 --- a/src/server/api/common/make-pagination-query.ts +++ b/src/server/api/common/make-pagination-query.ts @@ -1,6 +1,6 @@ import { SelectQueryBuilder } from 'typeorm'; -export function makePaginationQuery(q: SelectQueryBuilder, sinceId: string, untilId: string, sinceDate?: number, untilDate?: number) { +export function makePaginationQuery(q: SelectQueryBuilder, sinceId?: string, untilId?: string, sinceDate?: number, untilDate?: number) { if (sinceId && untilId) { q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); diff --git a/src/server/api/define.ts b/src/server/api/define.ts index a18419bcf6..990cbf2a86 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -5,9 +5,9 @@ import { ApiError } from './error'; import { App } from '../../models/entities/app'; type Params = { - [P in keyof T['params']]: T['params'][P]['transform'] extends Function - ? ReturnType - : ReturnType[0]; + [P in keyof T['params']]: NonNullable[P]['transform'] extends Function + ? ReturnType[P]['transform']> + : ReturnType[P]['validator']['get']>[0]; }; export type Response = Record | void; @@ -34,11 +34,11 @@ export default function (meta: T, cb: (params: Params(defs: T, params: any): [Params, ApiError] { +function getParams(defs: T, params: any): [Params, ApiError | null] { if (defs.params == null) return [params, null]; const x: any = {}; - let err: ApiError = null; + let err: ApiError | null = null; Object.entries(defs.params).some(([k, def]) => { const [v, e] = def.validator.get(params[k]); if (e) { diff --git a/src/server/api/endpoints/admin/abuse-user-reports.ts b/src/server/api/endpoints/admin/abuse-user-reports.ts index 5c5a734c1d..63d1dd795c 100644 --- a/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -29,7 +29,7 @@ export const meta = { export default define(meta, async (ps) => { const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); - const reports = await query.take(ps.limit).getMany(); + const reports = await query.take(ps.limit!).getMany(); return await AbuseUserReports.packMany(reports); }); diff --git a/src/server/api/endpoints/admin/drive/files.ts b/src/server/api/endpoints/admin/drive/files.ts index 1ccabc92d9..7c6672e6de 100644 --- a/src/server/api/endpoints/admin/drive/files.ts +++ b/src/server/api/endpoints/admin/drive/files.ts @@ -56,8 +56,8 @@ export default define(meta, async (ps, me) => { const files = await DriveFiles.find({ where: q, - take: ps.limit, - order: sort[ps.sort] || sort[fallback], + take: ps.limit!, + order: sort[ps.sort!] || sort[fallback], skip: ps.offset }); diff --git a/src/server/api/endpoints/admin/emoji/list.ts b/src/server/api/endpoints/admin/emoji/list.ts index 26385d4e23..cf73e4cc75 100644 --- a/src/server/api/endpoints/admin/emoji/list.ts +++ b/src/server/api/endpoints/admin/emoji/list.ts @@ -23,7 +23,7 @@ export const meta = { export default define(meta, async (ps) => { const emojis = await Emojis.find({ - host: toPuny(ps.host) + host: ps.host ? toPuny(ps.host) : null }); return emojis.map(e => ({ diff --git a/src/server/api/endpoints/admin/federation/remove-all-following.ts b/src/server/api/endpoints/admin/federation/remove-all-following.ts index fca76e7086..25aae6db88 100644 --- a/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import define from '../../../define'; import deleteFollowing from '../../../../../services/following/delete'; import { Followings, Users } from '../../../../../models'; +import { ensure } from '../../../../../prelude/ensure'; export const meta = { tags: ['admin'], @@ -22,13 +23,11 @@ export default define(meta, async (ps, me) => { }); const pairs = await Promise.all(followings.map(f => Promise.all([ - Users.findOne(f.followerId), - Users.findOne(f.followeeId) + Users.findOne(f.followerId).then(ensure), + Users.findOne(f.followeeId).then(ensure) ]))); for (const pair of pairs) { deleteFollowing(pair[0], pair[1]); } - - return; }); diff --git a/src/server/api/endpoints/admin/logs.ts b/src/server/api/endpoints/admin/logs.ts index 907b1fdc14..e1419bdfe1 100644 --- a/src/server/api/endpoints/admin/logs.ts +++ b/src/server/api/endpoints/admin/logs.ts @@ -65,7 +65,7 @@ export default define(meta, async (ps) => { } } - const logs = await query.orderBy('log.createdAt', 'DESC').take(ps.limit).getMany(); + const logs = await query.orderBy('log.createdAt', 'DESC').take(ps.limit!).getMany(); return logs; }); diff --git a/src/server/api/endpoints/admin/queue/jobs.ts b/src/server/api/endpoints/admin/queue/jobs.ts index c2496d7ef7..4e47775692 100644 --- a/src/server/api/endpoints/admin/queue/jobs.ts +++ b/src/server/api/endpoints/admin/queue/jobs.ts @@ -28,9 +28,9 @@ export default define(meta, async (ps) => { const queue = ps.domain === 'deliver' ? deliverQueue : ps.domain === 'inbox' ? inboxQueue : - null; + null as never; - const jobs = await queue.getJobs([ps.state], 0, ps.limit); + const jobs = await queue.getJobs([ps.state], 0, ps.limit!); return jobs.map(job => ({ id: job.id, diff --git a/src/server/api/endpoints/admin/show-users.ts b/src/server/api/endpoints/admin/show-users.ts index 73976b9872..97760ae797 100644 --- a/src/server/api/endpoints/admin/show-users.ts +++ b/src/server/api/endpoints/admin/show-users.ts @@ -82,7 +82,7 @@ export default define(meta, async (ps, me) => { default: query.orderBy('user.id', 'ASC'); break; } - query.take(ps.limit); + query.take(ps.limit!); query.skip(ps.offset); const users = await query.getMany(); diff --git a/src/server/api/endpoints/admin/update-remote-user.ts b/src/server/api/endpoints/admin/update-remote-user.ts index 0be9047d5a..f9716328d5 100644 --- a/src/server/api/endpoints/admin/update-remote-user.ts +++ b/src/server/api/endpoints/admin/update-remote-user.ts @@ -28,5 +28,5 @@ export const meta = { export default define(meta, async (ps) => { const user = await getRemoteUser(ps.userId); - await updatePerson(user.uri); + await updatePerson(user.uri!); }); diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index 35c5dd318d..1b992eeaa7 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -123,14 +123,14 @@ async function fetchAny(uri: string) { const note = await createNote(object.id); return { type: 'Note', - object: await Notes.pack(note, null, { detail: true }) + object: await Notes.pack(note!, null, { detail: true }) }; } return null; } -async function mergePack(user: User, note: Note) { +async function mergePack(user: User | null | undefined, note: Note | null | undefined) { if (user != null) { return { type: 'User', diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index 0baa6b111c..55898b59db 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -5,6 +5,7 @@ import define from '../../define'; import { ApiError } from '../../error'; import { AuthSessions, AccessTokens, Apps } from '../../../../models'; import { genId } from '../../../../misc/gen-id'; +import { ensure } from '../../../../prelude/ensure'; export const meta = { tags: ['auth'], @@ -48,7 +49,7 @@ export default define(meta, async (ps, user) => { if (exist == null) { // Lookup app - const app = await Apps.findOne(session.appId); + const app = await Apps.findOne(session.appId).then(ensure); // Generate Hash const sha256 = crypto.createHash('sha256'); diff --git a/src/server/api/endpoints/auth/session/userkey.ts b/src/server/api/endpoints/auth/session/userkey.ts index 8524b96f94..7126ac52c1 100644 --- a/src/server/api/endpoints/auth/session/userkey.ts +++ b/src/server/api/endpoints/auth/session/userkey.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import define from '../../../define'; import { ApiError } from '../../../error'; import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; +import { ensure } from '../../../../../prelude/ensure'; export const meta = { tags: ['auth'], @@ -90,7 +91,7 @@ export default define(meta, async (ps) => { const accessToken = await AccessTokens.findOne({ appId: app.id, userId: session.userId - }); + }).then(ensure); // Delete session AuthSessions.delete(session.id); diff --git a/src/server/api/endpoints/blocking/list.ts b/src/server/api/endpoints/blocking/list.ts index a078891ab0..97f353579d 100644 --- a/src/server/api/endpoints/blocking/list.ts +++ b/src/server/api/endpoints/blocking/list.ts @@ -44,7 +44,7 @@ export default define(meta, async (ps, me) => { .andWhere(`blocking.blockerId = :meId`, { meId: me.id }); const blockings = await query - .take(ps.limit) + .take(ps.limit!) .getMany(); return await Blockings.packMany(blockings, me); diff --git a/src/server/api/endpoints/charts/active-users.ts b/src/server/api/endpoints/charts/active-users.ts index 60fa72c5c7..f0349b17f3 100644 --- a/src/server/api/endpoints/charts/active-users.ts +++ b/src/server/api/endpoints/charts/active-users.ts @@ -33,5 +33,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await activeUsersChart.getChart(ps.span as any, ps.limit); + return await activeUsersChart.getChart(ps.span as any, ps.limit!); }); diff --git a/src/server/api/endpoints/charts/drive.ts b/src/server/api/endpoints/charts/drive.ts index a9676e1586..ae6d894407 100644 --- a/src/server/api/endpoints/charts/drive.ts +++ b/src/server/api/endpoints/charts/drive.ts @@ -33,5 +33,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await driveChart.getChart(ps.span as any, ps.limit); + return await driveChart.getChart(ps.span as any, ps.limit!); }); diff --git a/src/server/api/endpoints/charts/federation.ts b/src/server/api/endpoints/charts/federation.ts index 95f16c76a7..34e9bfee5f 100644 --- a/src/server/api/endpoints/charts/federation.ts +++ b/src/server/api/endpoints/charts/federation.ts @@ -33,5 +33,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await federationChart.getChart(ps.span as any, ps.limit); + return await federationChart.getChart(ps.span as any, ps.limit!); }); diff --git a/src/server/api/endpoints/charts/hashtag.ts b/src/server/api/endpoints/charts/hashtag.ts index a7ec12707e..eceb0b275c 100644 --- a/src/server/api/endpoints/charts/hashtag.ts +++ b/src/server/api/endpoints/charts/hashtag.ts @@ -40,5 +40,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await hashtagChart.getChart(ps.span as any, ps.limit, ps.tag); + return await hashtagChart.getChart(ps.span as any, ps.limit!, ps.tag); }); diff --git a/src/server/api/endpoints/charts/instance.ts b/src/server/api/endpoints/charts/instance.ts index cf3094f7e1..e99c17ae65 100644 --- a/src/server/api/endpoints/charts/instance.ts +++ b/src/server/api/endpoints/charts/instance.ts @@ -41,5 +41,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await instanceChart.getChart(ps.span as any, ps.limit, ps.host); + return await instanceChart.getChart(ps.span as any, ps.limit!, ps.host); }); diff --git a/src/server/api/endpoints/charts/network.ts b/src/server/api/endpoints/charts/network.ts index c0fcd95fe9..648588fbe5 100644 --- a/src/server/api/endpoints/charts/network.ts +++ b/src/server/api/endpoints/charts/network.ts @@ -33,5 +33,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await networkChart.getChart(ps.span as any, ps.limit); + return await networkChart.getChart(ps.span as any, ps.limit!); }); diff --git a/src/server/api/endpoints/charts/notes.ts b/src/server/api/endpoints/charts/notes.ts index 86f30e4b89..074c4978cd 100644 --- a/src/server/api/endpoints/charts/notes.ts +++ b/src/server/api/endpoints/charts/notes.ts @@ -33,5 +33,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await notesChart.getChart(ps.span as any, ps.limit); + return await notesChart.getChart(ps.span as any, ps.limit!); }); diff --git a/src/server/api/endpoints/charts/user/drive.ts b/src/server/api/endpoints/charts/user/drive.ts index e3696dfda1..918fb62c6a 100644 --- a/src/server/api/endpoints/charts/user/drive.ts +++ b/src/server/api/endpoints/charts/user/drive.ts @@ -42,5 +42,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await perUserDriveChart.getChart(ps.span as any, ps.limit, ps.userId); + return await perUserDriveChart.getChart(ps.span as any, ps.limit!, ps.userId); }); diff --git a/src/server/api/endpoints/charts/user/following.ts b/src/server/api/endpoints/charts/user/following.ts index 8feba0bd16..5d86e85d31 100644 --- a/src/server/api/endpoints/charts/user/following.ts +++ b/src/server/api/endpoints/charts/user/following.ts @@ -42,5 +42,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await perUserFollowingChart.getChart(ps.span as any, ps.limit, ps.userId); + return await perUserFollowingChart.getChart(ps.span as any, ps.limit!, ps.userId); }); diff --git a/src/server/api/endpoints/charts/user/notes.ts b/src/server/api/endpoints/charts/user/notes.ts index 8c1db54f76..d39a20df16 100644 --- a/src/server/api/endpoints/charts/user/notes.ts +++ b/src/server/api/endpoints/charts/user/notes.ts @@ -42,5 +42,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await perUserNotesChart.getChart(ps.span as any, ps.limit, ps.userId); + return await perUserNotesChart.getChart(ps.span as any, ps.limit!, ps.userId); }); diff --git a/src/server/api/endpoints/charts/user/reactions.ts b/src/server/api/endpoints/charts/user/reactions.ts index 7c9b2508ae..5b88a1d715 100644 --- a/src/server/api/endpoints/charts/user/reactions.ts +++ b/src/server/api/endpoints/charts/user/reactions.ts @@ -42,5 +42,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await perUserReactionsChart.getChart(ps.span as any, ps.limit, ps.userId); + return await perUserReactionsChart.getChart(ps.span as any, ps.limit!, ps.userId); }); diff --git a/src/server/api/endpoints/charts/users.ts b/src/server/api/endpoints/charts/users.ts index 3ed5e09349..17de5756da 100644 --- a/src/server/api/endpoints/charts/users.ts +++ b/src/server/api/endpoints/charts/users.ts @@ -33,5 +33,5 @@ export const meta = { }; export default define(meta, async (ps) => { - return await usersChart.getChart(ps.span as any, ps.limit); + return await usersChart.getChart(ps.span as any, ps.limit!); }); diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts index 400b73d3b7..4e4db6c780 100644 --- a/src/server/api/endpoints/drive/files.ts +++ b/src/server/api/endpoints/drive/files.ts @@ -66,7 +66,7 @@ export default define(meta, async (ps, user) => { } } - const files = await query.take(ps.limit).getMany(); + const files = await query.take(ps.limit!).getMany(); return await DriveFiles.packMany(files, { detail: false, self: true }); }); diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index 5702c70fc0..340a39a41c 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -92,6 +92,6 @@ export default define(meta, async (ps, user, app, file, cleanup) => { apiLogger.error(e); throw new ApiError(); } finally { - cleanup(); + cleanup!(); } }); diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts index b516ec2df6..e8c0e683c9 100644 --- a/src/server/api/endpoints/drive/files/show.ts +++ b/src/server/api/endpoints/drive/files/show.ts @@ -63,7 +63,7 @@ export const meta = { }; export default define(meta, async (ps, user) => { - let file: DriveFile; + let file: DriveFile | undefined; if (ps.fileId) { file = await DriveFiles.findOne(ps.fileId); @@ -81,14 +81,14 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.fileIdOrUrlRequired); } - if (!user.isAdmin && !user.isModerator && (file.userId !== user.id)) { - throw new ApiError(meta.errors.accessDenied); - } - if (file == null) { throw new ApiError(meta.errors.noSuchFile); } + if (!user.isAdmin && !user.isModerator && (file.userId !== user.id)) { + throw new ApiError(meta.errors.accessDenied); + } + return await DriveFiles.pack(file, { detail: true, self: true diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts index f5c3816407..08ae2ff709 100644 --- a/src/server/api/endpoints/drive/folders.ts +++ b/src/server/api/endpoints/drive/folders.ts @@ -54,7 +54,7 @@ export default define(meta, async (ps, user) => { query.andWhere('folder.parentId IS NULL'); } - const folders = await query.take(ps.limit).getMany(); + const folders = await query.take(ps.limit!).getMany(); return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); }); diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts index 90129bed63..7d3ece00a3 100644 --- a/src/server/api/endpoints/drive/folders/update.ts +++ b/src/server/api/endpoints/drive/folders/update.ts @@ -102,10 +102,10 @@ export default define(meta, async (ps, user) => { id: folderId }); - if (folder2.id === folder.id) { + if (folder2!.id === folder!.id) { return true; - } else if (folder2.parentId) { - return await checkCircle(folder2.parentId); + } else if (folder2!.parentId) { + return await checkCircle(folder2!.parentId); } else { return false; } diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts index 9a84627767..96d9f82421 100644 --- a/src/server/api/endpoints/drive/stream.ts +++ b/src/server/api/endpoints/drive/stream.ts @@ -50,7 +50,7 @@ export default define(meta, async (ps, user) => { } } - const files = await query.take(ps.limit).getMany(); + const files = await query.take(ps.limit!).getMany(); return await DriveFiles.packMany(files, { detail: false, self: true }); }); diff --git a/src/server/api/endpoints/federation/instances.ts b/src/server/api/endpoints/federation/instances.ts index 1946d26dec..301338ed96 100644 --- a/src/server/api/endpoints/federation/instances.ts +++ b/src/server/api/endpoints/federation/instances.ts @@ -86,7 +86,7 @@ export default define(meta, async (ps, me) => { } } - const instances = await query.take(ps.limit).skip(ps.offset).getMany(); + const instances = await query.take(ps.limit!).skip(ps.offset).getMany(); return instances; }); diff --git a/src/server/api/endpoints/games/reversi/games.ts b/src/server/api/endpoints/games/reversi/games.ts index 07736e0424..7267157e0e 100644 --- a/src/server/api/endpoints/games/reversi/games.ts +++ b/src/server/api/endpoints/games/reversi/games.ts @@ -41,7 +41,7 @@ export default define(meta, async (ps, user) => { } // Fetch games - const games = await query.take(ps.limit).getMany(); + const games = await query.take(ps.limit!).getMany(); return await Promise.all(games.map((g) => ReversiGames.pack(g, user, { detail: false diff --git a/src/server/api/endpoints/games/reversi/match.ts b/src/server/api/endpoints/games/reversi/match.ts index e34d3c67f4..da367b5978 100644 --- a/src/server/api/endpoints/games/reversi/match.ts +++ b/src/server/api/endpoints/games/reversi/match.ts @@ -70,7 +70,7 @@ export default define(meta, async (ps, user) => { map: eighteight.data, bw: 'random', isLlotheo: false - } as ReversiGame); + } as Partial); publishReversiStream(exist.parentId, 'matched', await ReversiGames.pack(game, exist.parentId)); @@ -107,6 +107,6 @@ export default define(meta, async (ps, user) => { publishReversiStream(child.id, 'invited', packed); publishMainStream(child.id, 'reversiInvited', packed); - return null; + return; } }); diff --git a/src/server/api/endpoints/hashtags/list.ts b/src/server/api/endpoints/hashtags/list.ts index 7996c81669..2998bc1a13 100644 --- a/src/server/api/endpoints/hashtags/list.ts +++ b/src/server/api/endpoints/hashtags/list.ts @@ -86,7 +86,7 @@ export default define(meta, async (ps, me) => { 'tag.attachedRemoteUsersCount', ]); - const tags = await query.take(ps.limit).getMany(); + const tags = await query.take(ps.limit!).getMany(); return tags; }); diff --git a/src/server/api/endpoints/hashtags/search.ts b/src/server/api/endpoints/hashtags/search.ts index fd91a2ebcc..6a9a2df6ef 100644 --- a/src/server/api/endpoints/hashtags/search.ts +++ b/src/server/api/endpoints/hashtags/search.ts @@ -48,7 +48,7 @@ export default define(meta, async (ps) => { const hashtags = await Hashtags.createQueryBuilder('tag') .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) .orderBy('tag.count', 'DESC') - .take(ps.limit) + .take(ps.limit!) .skip(ps.offset) .getMany(); diff --git a/src/server/api/endpoints/hashtags/users.ts b/src/server/api/endpoints/hashtags/users.ts index 20cba96d0e..fa58f2f2c0 100644 --- a/src/server/api/endpoints/hashtags/users.ts +++ b/src/server/api/endpoints/hashtags/users.ts @@ -79,7 +79,7 @@ export default define(meta, async (ps, me) => { case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break; } - const users = await query.take(ps.limit).getMany(); + const users = await query.take(ps.limit!).getMany(); return await Users.packMany(users, me, { detail: true }); }); diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts index edc7cefd26..e23678dcbb 100644 --- a/src/server/api/endpoints/i/2fa/done.ts +++ b/src/server/api/endpoints/i/2fa/done.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import * as speakeasy from 'speakeasy'; import define from '../../../define'; import { UserProfiles } from '../../../../../models'; +import { ensure } from '../../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -18,7 +19,7 @@ export const meta = { export default define(meta, async (ps, user) => { const token = ps.token.replace(/\s/g, ''); - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); if (profile.twoFactorTempSecret == null) { throw new Error('二段階認証の設定が開始されていません'); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index db9a2fe944..76d79b3a49 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -5,6 +5,7 @@ import * as QRCode from 'qrcode'; import config from '../../../../../config'; import define from '../../../define'; import { UserProfiles } from '../../../../../models'; +import { ensure } from '../../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -19,10 +20,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(ps.password, profile.password); + const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts index fa25b74391..9c7857e7ef 100644 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ b/src/server/api/endpoints/i/2fa/unregister.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles } from '../../../../../models'; +import { ensure } from '../../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -16,10 +17,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(ps.password, profile.password); + const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/authorized-apps.ts b/src/server/api/endpoints/i/authorized-apps.ts index ebf04fcb58..3e9fea19e2 100644 --- a/src/server/api/endpoints/i/authorized-apps.ts +++ b/src/server/api/endpoints/i/authorized-apps.ts @@ -31,7 +31,7 @@ export default define(meta, async (ps, user) => { where: { userId: user.id }, - take: ps.limit, + take: ps.limit!, skip: ps.offset, order: { id: ps.sort == 'asc' ? 1 : -1 diff --git a/src/server/api/endpoints/i/change-password.ts b/src/server/api/endpoints/i/change-password.ts index d0e0695e18..0dda125b9c 100644 --- a/src/server/api/endpoints/i/change-password.ts +++ b/src/server/api/endpoints/i/change-password.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; import { UserProfiles } from '../../../../models'; +import { ensure } from '../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -20,10 +21,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(ps.currentPassword, profile.password); + const same = await bcrypt.compare(ps.currentPassword, profile.password!); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts index 7ef7aa5fac..389d0b3212 100644 --- a/src/server/api/endpoints/i/delete-account.ts +++ b/src/server/api/endpoints/i/delete-account.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; import { Users, UserProfiles } from '../../../../models'; +import { ensure } from '../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -16,10 +17,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(ps.password, profile.password); + const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts index d2d149b2d1..2c25250bea 100644 --- a/src/server/api/endpoints/i/favorites.ts +++ b/src/server/api/endpoints/i/favorites.ts @@ -38,7 +38,7 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('favorite.note', 'note'); const favorites = await query - .take(ps.limit) + .take(ps.limit!) .getMany(); return await NoteFavorites.packMany(favorites, user); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 9b016e0a2d..56074c9d00 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -81,13 +81,13 @@ export default define(meta, async (ps, user) => { query.setParameters(followingQuery.getParameters()); } - if (ps.includeTypes.length > 0) { + if (ps.includeTypes!.length > 0) { query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes }); - } else if (ps.excludeTypes.length > 0) { + } else if (ps.excludeTypes!.length > 0) { query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); } - const notifications = await query.take(ps.limit).getMany(); + const notifications = await query.take(ps.limit!).getMany(); // Mark all as read if (notifications.length > 0 && ps.markAsRead) { diff --git a/src/server/api/endpoints/i/regenerate-token.ts b/src/server/api/endpoints/i/regenerate-token.ts index ec53bca979..56c0362c88 100644 --- a/src/server/api/endpoints/i/regenerate-token.ts +++ b/src/server/api/endpoints/i/regenerate-token.ts @@ -4,6 +4,7 @@ import { publishMainStream } from '../../../../services/stream'; import generateUserToken from '../../common/generate-native-user-token'; import define from '../../define'; import { Users, UserProfiles } from '../../../../models'; +import { ensure } from '../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -18,10 +19,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(ps.password, profile.password); + const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/signin-history.ts b/src/server/api/endpoints/i/signin-history.ts index e9ae19d734..74648951fd 100644 --- a/src/server/api/endpoints/i/signin-history.ts +++ b/src/server/api/endpoints/i/signin-history.ts @@ -29,7 +29,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) .andWhere(`signin.userId = :meId`, { meId: user.id }); - const history = await query.take(ps.limit).getMany(); + const history = await query.take(ps.limit!).getMany(); return await Promise.all(history.map(record => Signins.pack(record))); }); diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index d98f0d753e..8b05f3c8cb 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -9,6 +9,7 @@ import * as ms from 'ms'; import * as bcrypt from 'bcryptjs'; import { apiLogger } from '../../logger'; import { Users, UserProfiles } from '../../../../models'; +import { ensure } from '../../../../prelude/ensure'; export const meta = { requireCredential: true, @@ -32,10 +33,10 @@ export const meta = { }; export default define(meta, async (ps, user) => { - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(ps.password, profile.password); + const same = await bcrypt.compare(ps.password, profile.password!); if (!same) { throw new Error('incorrect password'); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 3cb514d939..d06ab621c6 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -206,13 +206,13 @@ export default define(meta, async (ps, user, app) => { if (updates.name != null) { const tokens = parsePlain(updates.name); - emojis = emojis.concat(extractEmojis(tokens)); + emojis = emojis.concat(extractEmojis(tokens!)); } if (profile.description != null) { const tokens = parse(profile.description); - emojis = emojis.concat(extractEmojis(tokens)); - tags = extractHashtags(tokens).map(tag => tag.toLowerCase()); + emojis = emojis.concat(extractEmojis(tokens!)); + tags = extractHashtags(tokens!).map(tag => tag.toLowerCase()); } updates.emojis = emojis; diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts index c0aec61212..c12378eb7e 100644 --- a/src/server/api/endpoints/messaging/history.ts +++ b/src/server/api/endpoints/messaging/history.ts @@ -38,7 +38,7 @@ export default define(meta, async (ps, user) => { const history: MessagingMessage[] = []; - for (let i = 0; i < ps.limit; i++) { + for (let i = 0; i < ps.limit!; i++) { const found = history.map(m => (m.userId === user.id) ? m.recipientId : m.userId); const query = MessagingMessages.createQueryBuilder('message') diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts index 5f2d184472..0fd8a4860d 100644 --- a/src/server/api/endpoints/mute/list.ts +++ b/src/server/api/endpoints/mute/list.ts @@ -44,7 +44,7 @@ export default define(meta, async (ps, me) => { .andWhere(`muting.muterId = :meId`, { meId: me.id }); const mutings = await query - .take(ps.limit) + .take(ps.limit!) .getMany(); return await Mutings.packMany(mutings, me); diff --git a/src/server/api/endpoints/my/apps.ts b/src/server/api/endpoints/my/apps.ts index d205d1674c..e8b26362a4 100644 --- a/src/server/api/endpoints/my/apps.ts +++ b/src/server/api/endpoints/my/apps.ts @@ -32,7 +32,7 @@ export default define(meta, async (ps, user) => { const apps = await Apps.find({ where: query, - take: ps.limit, + take: ps.limit!, skip: ps.offset, }); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 10f6e39845..17ba969350 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -100,7 +100,7 @@ export default define(meta, async (ps) => { // query.isBot = bot; //} - const notes = await query.take(ps.limit).getMany(); + const notes = await query.take(ps.limit!).getMany(); return await Notes.packMany(notes); }); diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts index 72f2c39d6a..2b4ae2a312 100644 --- a/src/server/api/endpoints/notes/children.ts +++ b/src/server/api/endpoints/notes/children.ts @@ -66,7 +66,7 @@ export default define(meta, async (ps, user) => { if (user) generateVisibilityQuery(query, user); if (user) generateMuteQuery(query, user); - const notes = await query.take(ps.limit).getMany(); + const notes = await query.take(ps.limit!).getMany(); return await Notes.packMany(notes, user); }); diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts index 6defd79042..6b26e31c07 100644 --- a/src/server/api/endpoints/notes/conversation.ts +++ b/src/server/api/endpoints/notes/conversation.ts @@ -64,12 +64,13 @@ export default define(meta, async (ps, user) => { async function get(id: any) { i++; const p = await Notes.findOne(id); + if (p == null) return; - if (i > ps.offset) { + if (i > ps.offset!) { conversation.push(p); } - if (conversation.length == ps.limit) { + if (conversation.length == ps.limit!) { return; } diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 138f05fb3b..83649015d8 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -225,23 +225,24 @@ export const meta = { export default define(meta, async (ps, user, app) => { let visibleUsers: User[] = []; if (ps.visibleUserIds) { - visibleUsers = await Promise.all(ps.visibleUserIds.map(id => Users.findOne(id))); + visibleUsers = (await Promise.all(ps.visibleUserIds.map(id => Users.findOne(id)))) + .filter(x => x != null) as User[]; } let files: DriveFile[] = []; const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null; if (fileIds != null) { - files = await Promise.all(fileIds.map(fileId => { - return DriveFiles.findOne({ + files = (await Promise.all(fileIds.map(fileId => + DriveFiles.findOne({ id: fileId, userId: user.id - }); - })); + }) + ))).filter(file => file != null) as DriveFile[]; - files = files.filter(file => file != null); + files = files; } - let renote: Note = null; + let renote: Note | undefined; if (ps.renoteId != null) { // Fetch renote to note renote = await Notes.findOne(ps.renoteId); @@ -253,7 +254,7 @@ export default define(meta, async (ps, user, app) => { } } - let reply: Note = null; + let reply: Note | undefined; if (ps.replyId != null) { // Fetch reply reply = await Notes.findOne(ps.replyId); @@ -290,8 +291,8 @@ export default define(meta, async (ps, user, app) => { choices: ps.poll.choices, multiple: ps.poll.multiple || false, expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null - } : null, - text: ps.text, + } : undefined, + text: ps.text || undefined, reply, renote, cw: ps.cw, @@ -300,9 +301,9 @@ export default define(meta, async (ps, user, app) => { localOnly: ps.localOnly, visibility: ps.visibility, visibleUsers, - apMentions: ps.noExtractMentions ? [] : null, - apHashtags: ps.noExtractHashtags ? [] : null, - apEmojis: ps.noExtractEmojis ? [] : null, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, geo: ps.geo }); diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts index dbaf91bca3..17d44c424d 100644 --- a/src/server/api/endpoints/notes/delete.ts +++ b/src/server/api/endpoints/notes/delete.ts @@ -6,6 +6,7 @@ import * as ms from 'ms'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; import { Users } from '../../../../models'; +import { ensure } from '../../../../prelude/ensure'; export const meta = { stability: 'stable', @@ -63,5 +64,5 @@ export default define(meta, async (ps, user) => { } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await deleteNote(await Users.findOne(note.userId), note); + await deleteNote(await Users.findOne(note.userId).then(ensure), note); }); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index c44a5275bb..5d2e5b7d44 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -41,7 +41,7 @@ export default define(meta, async (ps, user) => { if (user) generateMuteQuery(query, user); - const notes = await query.orderBy('note.score', 'DESC').take(ps.limit).getMany(); + const notes = await query.orderBy('note.score', 'DESC').take(ps.limit!).getMany(); return await Notes.packMany(notes, user); }); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 7bf62f366b..ceffb1cf4a 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -84,7 +84,7 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 76f1682a95..6dfb143003 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -185,7 +185,7 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index cd07341342..c10c0d7482 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -100,11 +100,11 @@ export default define(meta, async (ps, user) => { query.andWhere('note.fileIds != \'{}\''); } - if (ps.fileType) { + if (ps.fileType != null) { query.andWhere('note.fileIds != \'{}\''); query.andWhere(new Brackets(qb => { - for (const type of ps.fileType) { - const i = ps.fileType.indexOf(type); + for (const type of ps.fileType!) { + const i = ps.fileType!.indexOf(type); qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); } })); @@ -120,7 +120,7 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); if (user) { activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 0bbe7d3327..b7f614915b 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -74,7 +74,7 @@ export default define(meta, async (ps, user) => { query.setParameters(followingQuery.getParameters()); } - const mentions = await query.take(ps.limit).getMany(); + const mentions = await query.take(ps.limit!).getMany(); for (const note of mentions) { read(user.id, note.id); diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts index ff838d4f4f..cbd4d35cd4 100644 --- a/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/src/server/api/endpoints/notes/polls/recommendation.ts @@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => { query.setParameters(mutingQuery.getParameters()); //#endregion - const polls = await query.take(ps.limit).skip(ps.offset).getMany(); + const polls = await query.take(ps.limit!).skip(ps.offset).getMany(); if (polls.length === 0) return []; diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index dd4d93c7ac..68dee66828 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -14,6 +14,7 @@ import { PollVotes, NoteWatchings, Users, Polls, UserProfiles } from '../../../. import { Not } from 'typeorm'; import { IRemoteUser } from '../../../../../models/entities/user'; import { genId } from '../../../../../misc/gen-id'; +import { ensure } from '../../../../../prelude/ensure'; export const meta = { desc: { @@ -87,7 +88,7 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.noPoll); } - const poll = await Polls.findOne({ noteId: note.id }); + const poll = await Polls.findOne({ noteId: note.id }).then(ensure); if (poll.expiresAt && poll.expiresAt < createdAt) { throw new ApiError(meta.errors.alreadyExpired); @@ -149,7 +150,7 @@ export default define(meta, async (ps, user) => { } }); - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // この投稿をWatchする if (profile.autoWatch !== false) { @@ -158,7 +159,7 @@ export default define(meta, async (ps, user) => { // リモート投票の場合リプライ送信 if (note.userHost != null) { - const pollOwner: IRemoteUser = await Users.findOne(note.userId); + const pollOwner = await Users.findOne(note.userId).then(ensure) as IRemoteUser; deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox); } diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts index b1b5ca9d33..bcb0b6d1ec 100644 --- a/src/server/api/endpoints/notes/reactions.ts +++ b/src/server/api/endpoints/notes/reactions.ts @@ -71,7 +71,7 @@ export default define(meta, async (ps, user) => { const reactions = await NoteReactions.find({ where: query, - take: ps.limit, + take: ps.limit!, skip: ps.offset, order: { id: -1 diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 81b899836d..74a8ea918e 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -70,7 +70,7 @@ export default define(meta, async (ps, user) => { if (user) generateVisibilityQuery(query, user); if (user) generateMuteQuery(query, user); - const renotes = await query.take(ps.limit).getMany(); + const renotes = await query.take(ps.limit!).getMany(); return await Notes.packMany(renotes, user); }); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 09b0f17164..980ff2446e 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -61,7 +61,7 @@ export default define(meta, async (ps, user) => { if (user) generateVisibilityQuery(query, user); if (user) generateMuteQuery(query, user); - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); return await Notes.packMany(timeline, user); }); diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index 48de88d36e..cba3724b6f 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -100,7 +100,7 @@ export default define(meta, async (ps, me) => { } else { let i = 0; query.andWhere(new Brackets(qb => { - for (const tags of ps.query) { + for (const tags of ps.query!) { qb.orWhere(new Brackets(qb => { for (const tag of tags) { qb.andWhere(`:tag${i} = ANY(note.tags)`, { [`tag${i}`]: tag }); @@ -140,7 +140,7 @@ export default define(meta, async (ps, me) => { } // Search notes - const notes = await query.take(ps.limit).getMany(); + const notes = await query.take(ps.limit!).getMany(); return await Notes.packMany(notes, me); }); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index cc88fb9380..4d5ac6fbe0 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -54,7 +54,7 @@ export default define(meta, async (ps, me) => { index: 'misskey', type: 'note', body: { - size: ps.limit, + size: ps.limit!, from: ps.offset, query: { simple_query_string: { diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index e22db4d1b0..c27f3df1b7 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -171,7 +171,7 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); activeUsersChart.update(user); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index deda04acb4..05f171af8b 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -189,7 +189,7 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); activeUsersChart.update(user); diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts index f99165f3d5..18af0a2685 100644 --- a/src/server/api/endpoints/users.ts +++ b/src/server/api/endpoints/users.ts @@ -88,7 +88,7 @@ export default define(meta, async (ps, me) => { if (me) generateMuteQueryForUsers(query, me); - query.take(ps.limit); + query.take(ps.limit!); query.skip(ps.offset); const users = await query.getMany(); diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 64d63e2d03..88a474be7c 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -66,7 +66,7 @@ export const meta = { export default define(meta, async (ps, me) => { const user = await Users.findOne(ps.userId != null ? { id: ps.userId } - : { usernameLower: ps.username.toLowerCase(), host: toPuny(ps.host) }); + : { usernameLower: ps.username!.toLowerCase(), host: toPuny(ps.host!) }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); @@ -76,7 +76,7 @@ export default define(meta, async (ps, me) => { .andWhere(`following.followeeId = :userId`, { userId: user.id }); const followings = await query - .take(ps.limit) + .take(ps.limit!) .getMany(); return await Followings.packMany(followings, me, { populateFollower: true }); diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 0e28001680..5e017150e8 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -66,7 +66,7 @@ export const meta = { export default define(meta, async (ps, me) => { const user = await Users.findOne(ps.userId != null ? { id: ps.userId } - : { usernameLower: ps.username.toLowerCase(), host: toPuny(ps.host) }); + : { usernameLower: ps.username!.toLowerCase(), host: toPuny(ps.host!) }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); @@ -76,7 +76,7 @@ export default define(meta, async (ps, me) => { .andWhere(`following.followerId = :userId`, { userId: user.id }); const followings = await query - .take(ps.limit) + .take(ps.limit!) .getMany(); return await Followings.packMany(followings, me, { populateFollowee: true }); diff --git a/src/server/api/endpoints/users/get-frequently-replied-users.ts b/src/server/api/endpoints/users/get-frequently-replied-users.ts index f82f437629..a1d140c6c9 100644 --- a/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -94,7 +94,7 @@ export default define(meta, async (ps, me) => { const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); // Extract top replied users - const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); + const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit!); // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 6df394cbb1..da23be3c55 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -153,11 +153,11 @@ export default define(meta, async (ps, me) => { query.andWhere('note.fileIds != \'{}\''); } - if (ps.fileType) { + if (ps.fileType != null) { query.andWhere('note.fileIds != \'{}\''); query.andWhere(new Brackets(qb => { - for (const type of ps.fileType) { - const i = ps.fileType.indexOf(type); + for (const type of ps.fileType!) { + const i = ps.fileType!.indexOf(type); qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); } })); @@ -194,7 +194,7 @@ export default define(meta, async (ps, me) => { //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit!).getMany(); return await Notes.packMany(timeline, user); }); diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 2c82d6613e..9e16e34e39 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -53,7 +53,7 @@ export default define(meta, async (ps, me) => { query.setParameters(followingQuery.getParameters()); - const users = await query.take(ps.limit).skip(ps.offset).getMany(); + const users = await query.take(ps.limit!).skip(ps.offset).getMany(); return await Users.packMany(users, me, { detail: true }); }); diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index 443bd507f3..96da221d97 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -71,16 +71,16 @@ export default define(meta, async (ps, me) => { .where('user.host IS NULL') .andWhere('user.isSuspended = FALSE') .andWhere('user.usernameLower like :username', { username: ps.query.replace('@', '').toLowerCase() + '%' }) - .take(ps.limit) + .take(ps.limit!) .skip(ps.offset) .getMany(); - if (users.length < ps.limit && !ps.localOnly) { + if (users.length < ps.limit! && !ps.localOnly) { const otherUsers = await Users.createQueryBuilder('user') .where('user.host IS NOT NULL') .andWhere('user.isSuspended = FALSE') .andWhere('user.usernameLower like :username', { username: ps.query.replace('@', '').toLowerCase() + '%' }) - .take(ps.limit - users.length) + .take(ps.limit! - users.length) .getMany(); users = users.concat(otherUsers); diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index ae93e40eaa..2be193f89b 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -74,7 +74,7 @@ export default define(meta, async (ps, me) => { }))); } else { // Lookup user - if (typeof ps.host === 'string') { + if (typeof ps.host === 'string' && typeof ps.username === 'string') { user = await resolveUser(ps.username, ps.host).catch(e => { apiLogger.warn(`failed to resolve remote user: ${e}`); throw new ApiError(meta.errors.failedToResolveRemoteUser); @@ -82,7 +82,7 @@ export default define(meta, async (ps, me) => { } else { const q: any = ps.userId != null ? { id: ps.userId } - : { usernameLower: ps.username.toLowerCase(), host: null }; + : { usernameLower: ps.username!.toLowerCase(), host: null }; user = await Users.findOne(q); } @@ -94,7 +94,7 @@ export default define(meta, async (ps, me) => { // ユーザー情報更新 if (Users.isRemoteUser(user)) { if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { - resolveUser(ps.username, ps.host, { }, true); + resolveUser(user.username, user.host, { }, true); } } diff --git a/src/server/api/error.ts b/src/server/api/error.ts index ca441d5811..cb0bdc9f47 100644 --- a/src/server/api/error.ts +++ b/src/server/api/error.ts @@ -1,3 +1,5 @@ +type E = { message: string, code: string, id: string, kind?: 'client' | 'server', httpStatusCode?: number }; + export class ApiError extends Error { public message: string; public code: string; @@ -6,7 +8,7 @@ export class ApiError extends Error { public httpStatusCode?: number; public info?: any; - constructor(e?: { message: string, code: string, id: string, kind?: 'client' | 'server', httpStatusCode?: number }, info?: any) { + constructor(e?: E | null | undefined, info?: any | null | undefined) { if (e == null) e = { message: 'Internal error occurred. Please contact us if the error persists.', code: 'INTERNAL_ERROR', diff --git a/src/server/api/limiter.ts b/src/server/api/limiter.ts index e29c061337..8a6c94458e 100644 --- a/src/server/api/limiter.ts +++ b/src/server/api/limiter.ts @@ -14,7 +14,7 @@ export default (endpoint: IEndpoint, user: User) => new Promise((ok, reject) => return; } - const limitation = endpoint.meta.limit; + const limitation = endpoint.meta.limit!; const key = limitation.hasOwnProperty('key') ? limitation.key @@ -41,7 +41,7 @@ export default (endpoint: IEndpoint, user: User) => new Promise((ok, reject) => id: `${user.id}:${key}:min`, duration: limitation.minInterval, max: 1, - db: limiterDB + db: limiterDB! }); minIntervalLimiter.get((err, info) => { @@ -69,7 +69,7 @@ export default (endpoint: IEndpoint, user: User) => new Promise((ok, reject) => id: `${user.id}:${key}`, duration: limitation.duration, max: limitation.max, - db: limiterDB + db: limiterDB! }); limiter.get((err, info) => { diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index fe2e5577c2..676546f2aa 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -7,6 +7,7 @@ import config from '../../../config'; import { Users, Signins, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; import { genId } from '../../../misc/gen-id'; +import { ensure } from '../../../prelude/ensure'; export default async (ctx: Koa.BaseContext) => { ctx.set('Access-Control-Allow-Origin', config.url); @@ -45,10 +46,10 @@ export default async (ctx: Koa.BaseContext) => { return; } - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); // Compare password - const same = await bcrypt.compare(password, profile.password); + const same = await bcrypt.compare(password, profile.password!); if (same) { if (profile.twoFactorEnabled) { diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index 03d83efd94..ea4df060f8 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -21,7 +21,7 @@ export default async (ctx: Koa.BaseContext) => { // Verify recaptcha // ただしテスト時はこの機構は障害となるため無効にする - if (process.env.NODE_ENV !== 'test' && instance.enableRecaptcha) { + if (process.env.NODE_ENV !== 'test' && instance.enableRecaptcha && instance.recaptchaSecretKey) { recaptcha.init({ secret_key: instance.recaptchaSecretKey }); @@ -100,7 +100,7 @@ export default async (ctx: Koa.BaseContext) => { e ? j(e) : s([publicKey, privateKey]) )); - let account: User; + let account!: User; // Start transaction await getConnection().transaction(async transactionalEntityManager => { diff --git a/src/server/api/service/discord.ts b/src/server/api/service/discord.ts index 879b8b4849..b2561dee61 100644 --- a/src/server/api/service/discord.ts +++ b/src/server/api/service/discord.ts @@ -10,6 +10,7 @@ import signin from '../common/signin'; import fetchMeta from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; +import { ensure } from '../../../prelude/ensure'; function getUserToken(ctx: Koa.BaseContext) { return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; @@ -43,7 +44,7 @@ router.get('/disconnect/discord', async ctx => { const user = await Users.findOne({ host: null, token: userToken - }); + }).then(ensure); await UserProfiles.update({ userId: user.id @@ -71,8 +72,8 @@ async function getOAuth2() { if (meta.enableDiscordIntegration) { return new OAuth2( - meta.discordClientId, - meta.discordClientSecret, + meta.discordClientId!, + meta.discordClientSecret!, 'https://discordapp.com/', 'api/oauth2/authorize', 'api/oauth2/token'); @@ -82,6 +83,8 @@ async function getOAuth2() { } router.get('/connect/discord', async ctx => { + if (redis == null) return; + if (!compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; @@ -103,10 +106,12 @@ router.get('/connect/discord', async ctx => { redis.set(userToken, JSON.stringify(params)); const oauth2 = await getOAuth2(); - ctx.redirect(oauth2.getAuthorizeUrl(params)); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); }); router.get('/signin/discord', async ctx => { + if (redis == null) return; + const sessid = uuid(); const params = { @@ -129,10 +134,12 @@ router.get('/signin/discord', async ctx => { redis.set(sessid, JSON.stringify(params)); const oauth2 = await getOAuth2(); - ctx.redirect(oauth2.getAuthorizeUrl(params)); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); }); router.get('/dc/cb', async ctx => { + if (redis == null) return; + const userToken = getUserToken(ctx); const oauth2 = await getOAuth2(); @@ -153,7 +160,7 @@ router.get('/dc/cb', async ctx => { } const { redirect_uri, state } = await new Promise((res, rej) => { - redis.get(sessid, async (_, state) => { + redis!.get(sessid, async (_, state) => { res(JSON.parse(state)); }); }); @@ -164,24 +171,22 @@ router.get('/dc/cb', async ctx => { } const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2.getOAuthAccessToken( - code, - { - grant_type: 'authorization_code', - redirect_uri - }, - (err, accessToken, refreshToken, result) => { - if (err) - rej(err); - else if (result.error) - rej(result.error); - else + oauth2!.getOAuthAccessToken(code, { + grant_type: 'authorization_code', + redirect_uri + }, (err, accessToken, refreshToken, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { res({ accessToken, refreshToken, expiresDate: Date.now() + Number(result.expires_in) * 1000 }); - })); + } + })); const { id, username, discriminator } = await new Promise((res, rej) => request({ @@ -191,10 +196,11 @@ router.get('/dc/cb', async ctx => { 'User-Agent': config.userAgent } }, (err, response, body) => { - if (err) + if (err) { rej(err); - else + } else { res(JSON.parse(body)); + } })); if (!id || !username || !discriminator) { @@ -235,7 +241,7 @@ router.get('/dc/cb', async ctx => { } const { redirect_uri, state } = await new Promise((res, rej) => { - redis.get(userToken, async (_, state) => { + redis!.get(userToken, async (_, state) => { res(JSON.parse(state)); }); }); @@ -246,24 +252,22 @@ router.get('/dc/cb', async ctx => { } const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2.getOAuthAccessToken( - code, - { - grant_type: 'authorization_code', - redirect_uri - }, - (err, accessToken, refreshToken, result) => { - if (err) - rej(err); - else if (result.error) - rej(result.error); - else - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000 - }); - })); + oauth2!.getOAuthAccessToken(code, { + grant_type: 'authorization_code', + redirect_uri + }, (err, accessToken, refreshToken, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { + res({ + accessToken, + refreshToken, + expiresDate: Date.now() + Number(result.expires_in) * 1000 + }); + } + })); const { id, username, discriminator } = await new Promise((res, rej) => request({ @@ -273,10 +277,11 @@ router.get('/dc/cb', async ctx => { 'User-Agent': config.userAgent } }, (err, response, body) => { - if (err) + if (err) { rej(err); - else + } else { res(JSON.parse(body)); + } })); if (!id || !username || !discriminator) { @@ -287,7 +292,7 @@ router.get('/dc/cb', async ctx => { const user = await Users.findOne({ host: null, token: userToken - }); + }).then(ensure); await UserProfiles.update({ userId: user.id }, { discord: true, diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index 580947811b..4878d8c529 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -10,6 +10,7 @@ import signin from '../common/signin'; import fetchMeta from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; +import { ensure } from '../../../prelude/ensure'; function getUserToken(ctx: Koa.BaseContext) { return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; @@ -43,7 +44,7 @@ router.get('/disconnect/github', async ctx => { const user = await Users.findOne({ host: null, token: userToken - }); + }).then(ensure); await UserProfiles.update({ userId: user.id @@ -66,7 +67,7 @@ router.get('/disconnect/github', async ctx => { async function getOath2() { const meta = await fetchMeta(); - if (meta.enableGithubIntegration) { + if (meta.enableGithubIntegration && meta.githubClientId && meta.githubClientSecret) { return new OAuth2( meta.githubClientId, meta.githubClientSecret, @@ -79,6 +80,8 @@ async function getOath2() { } router.get('/connect/github', async ctx => { + if (redis == null) return; + if (!compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; @@ -99,10 +102,12 @@ router.get('/connect/github', async ctx => { redis.set(userToken, JSON.stringify(params)); const oauth2 = await getOath2(); - ctx.redirect(oauth2.getAuthorizeUrl(params)); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); }); router.get('/signin/github', async ctx => { + if (redis == null) return; + const sessid = uuid(); const params = { @@ -124,10 +129,12 @@ router.get('/signin/github', async ctx => { redis.set(sessid, JSON.stringify(params)); const oauth2 = await getOath2(); - ctx.redirect(oauth2.getAuthorizeUrl(params)); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); }); router.get('/gh/cb', async ctx => { + if (redis == null) return; + const userToken = getUserToken(ctx); const oauth2 = await getOath2(); @@ -148,7 +155,7 @@ router.get('/gh/cb', async ctx => { } const { redirect_uri, state } = await new Promise((res, rej) => { - redis.get(sessid, async (_, state) => { + redis!.get(sessid, async (_, state) => { res(JSON.parse(state)); }); }); @@ -159,17 +166,17 @@ router.get('/gh/cb', async ctx => { } const { accessToken } = await new Promise((res, rej) => - oauth2.getOAuthAccessToken( - code, - { redirect_uri }, - (err, accessToken, refresh, result) => { - if (err) - rej(err); - else if (result.error) - rej(result.error); - else - res({ accessToken }); - })); + oauth2!.getOAuthAccessToken(code, { + redirect_uri + }, (err, accessToken, refresh, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { + res({ accessToken }); + } + })); const { login, id } = await new Promise((res, rej) => request({ @@ -215,7 +222,7 @@ router.get('/gh/cb', async ctx => { } const { redirect_uri, state } = await new Promise((res, rej) => { - redis.get(userToken, async (_, state) => { + redis!.get(userToken, async (_, state) => { res(JSON.parse(state)); }); }); @@ -226,7 +233,7 @@ router.get('/gh/cb', async ctx => { } const { accessToken } = await new Promise((res, rej) => - oauth2.getOAuthAccessToken( + oauth2!.getOAuthAccessToken( code, { redirect_uri }, (err, accessToken, refresh, result) => { @@ -261,7 +268,7 @@ router.get('/gh/cb', async ctx => { const user = await Users.findOne({ host: null, token: userToken - }); + }).then(ensure); await UserProfiles.update({ userId: user.id }, { github: true, diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts index c0c762c6c3..b35ee8c479 100644 --- a/src/server/api/service/twitter.ts +++ b/src/server/api/service/twitter.ts @@ -9,6 +9,7 @@ import signin from '../common/signin'; import fetchMeta from '../../../misc/fetch-meta'; import { Users, UserProfiles } from '../../../models'; import { ILocalUser } from '../../../models/entities/user'; +import { ensure } from '../../../prelude/ensure'; function getUserToken(ctx: Koa.BaseContext) { return ((ctx.headers['cookie'] || '').match(/i=(!\w+)/) || [null, null])[1]; @@ -42,7 +43,7 @@ router.get('/disconnect/twitter', async ctx => { const user = await Users.findOne({ host: null, token: userToken - }); + }).then(ensure); await UserProfiles.update({ userId: user.id @@ -66,7 +67,7 @@ router.get('/disconnect/twitter', async ctx => { async function getTwAuth() { const meta = await fetchMeta(); - if (meta.enableTwitterIntegration) { + if (meta.enableTwitterIntegration && meta.twitterConsumerKey && meta.twitterConsumerSecret) { return autwh({ consumerKey: meta.twitterConsumerKey, consumerSecret: meta.twitterConsumerSecret, @@ -78,6 +79,8 @@ async function getTwAuth() { } router.get('/connect/twitter', async ctx => { + if (redis == null) return; + if (!compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; @@ -90,14 +93,16 @@ router.get('/connect/twitter', async ctx => { } const twAuth = await getTwAuth(); - const twCtx = await twAuth.begin(); + const twCtx = await twAuth!.begin(); redis.set(userToken, JSON.stringify(twCtx)); ctx.redirect(twCtx.url); }); router.get('/signin/twitter', async ctx => { + if (redis == null) return; + const twAuth = await getTwAuth(); - const twCtx = await twAuth.begin(); + const twCtx = await twAuth!.begin(); const sessid = uuid(); @@ -117,6 +122,8 @@ router.get('/signin/twitter', async ctx => { }); router.get('/tw/cb', async ctx => { + if (redis == null) return; + const userToken = getUserToken(ctx); const twAuth = await getTwAuth(); @@ -130,14 +137,14 @@ router.get('/tw/cb', async ctx => { } const get = new Promise((res, rej) => { - redis.get(sessid, async (_, twCtx) => { + redis!.get(sessid, async (_, twCtx) => { res(twCtx); }); }); const twCtx = await get; - const result = await twAuth.done(JSON.parse(twCtx), ctx.query.oauth_verifier); + const result = await twAuth!.done(JSON.parse(twCtx), ctx.query.oauth_verifier); const link = await UserProfiles.createQueryBuilder() .where('twitter @> :twitter', { @@ -163,19 +170,19 @@ router.get('/tw/cb', async ctx => { } const get = new Promise((res, rej) => { - redis.get(userToken, async (_, twCtx) => { + redis!.get(userToken, async (_, twCtx) => { res(twCtx); }); }); const twCtx = await get; - const result = await twAuth.done(JSON.parse(twCtx), verifier); + const result = await twAuth!.done(JSON.parse(twCtx), verifier); const user = await Users.findOne({ host: null, token: userToken - }); + }).then(ensure); await UserProfiles.update({ userId: user.id }, { twitter: true, diff --git a/src/server/api/stream/channels/admin.ts b/src/server/api/stream/channels/admin.ts index e2eba10f78..1ff932d1dd 100644 --- a/src/server/api/stream/channels/admin.ts +++ b/src/server/api/stream/channels/admin.ts @@ -9,7 +9,7 @@ export default class extends Channel { @autobind public async init(params: any) { // Subscribe admin stream - this.subscriber.on(`adminStream:${this.user.id}`, data => { + this.subscriber.on(`adminStream:${this.user!.id}`, data => { this.send(data); }); } diff --git a/src/server/api/stream/channels/drive.ts b/src/server/api/stream/channels/drive.ts index 671aad4366..4112dd9b04 100644 --- a/src/server/api/stream/channels/drive.ts +++ b/src/server/api/stream/channels/drive.ts @@ -9,7 +9,7 @@ export default class extends Channel { @autobind public async init(params: any) { // Subscribe drive stream - this.subscriber.on(`driveStream:${this.user.id}`, data => { + this.subscriber.on(`driveStream:${this.user!.id}`, data => { this.send(data); }); } diff --git a/src/server/api/stream/channels/games/reversi-game.ts b/src/server/api/stream/channels/games/reversi-game.ts index 158f108c4e..d708eae9f7 100644 --- a/src/server/api/stream/channels/games/reversi-game.ts +++ b/src/server/api/stream/channels/games/reversi-game.ts @@ -12,7 +12,7 @@ export default class extends Channel { public static shouldShare = false; public static requireCredential = false; - private gameId: ReversiGame['id']; + private gameId: ReversiGame['id'] | null = null; @autobind public async init(params: any) { @@ -40,7 +40,10 @@ export default class extends Channel { @autobind private async updateSettings(key: string, value: any) { - const game = await ReversiGames.findOne(this.gameId); + if (this.user == null) return; + + const game = await ReversiGames.findOne(this.gameId!); + if (game == null) throw 'game not found'; if (game.isStarted) return; if ((game.user1Id !== this.user.id) && (game.user2Id !== this.user.id)) return; @@ -49,11 +52,11 @@ export default class extends Channel { if (!['map', 'bw', 'isLlotheo', 'canPutEverywhere', 'loopedBoard'].includes(key)) return; - await ReversiGames.update({ id: this.gameId }, { + await ReversiGames.update(this.gameId!, { [key]: value }); - publishReversiGameStream(this.gameId, 'updateSettings', { + publishReversiGameStream(this.gameId!, 'updateSettings', { key: key, value: value }); @@ -61,7 +64,10 @@ export default class extends Channel { @autobind private async initForm(form: any) { - const game = await ReversiGames.findOne(this.gameId); + if (this.user == null) return; + + const game = await ReversiGames.findOne(this.gameId!); + if (game == null) throw 'game not found'; if (game.isStarted) return; if ((game.user1Id !== this.user.id) && (game.user2Id !== this.user.id)) return; @@ -72,9 +78,9 @@ export default class extends Channel { form2: form }; - await ReversiGames.update({ id: this.gameId }, set); + await ReversiGames.update(this.gameId!, set); - publishReversiGameStream(this.gameId, 'initForm', { + publishReversiGameStream(this.gameId!, 'initForm', { userId: this.user.id, form }); @@ -82,7 +88,10 @@ export default class extends Channel { @autobind private async updateForm(id: string, value: any) { - const game = await ReversiGames.findOne({ id: this.gameId }); + if (this.user == null) return; + + const game = await ReversiGames.findOne(this.gameId!); + if (game == null) throw 'game not found'; if (game.isStarted) return; if ((game.user1Id !== this.user.id) && (game.user2Id !== this.user.id)) return; @@ -101,9 +110,9 @@ export default class extends Channel { form1: form }; - await ReversiGames.update({ id: this.gameId }, set); + await ReversiGames.update(this.gameId!, set); - publishReversiGameStream(this.gameId, 'updateForm', { + publishReversiGameStream(this.gameId!, 'updateForm', { userId: this.user.id, id, value @@ -112,8 +121,10 @@ export default class extends Channel { @autobind private async message(message: any) { + if (this.user == null) return; + message.id = Math.random(); - publishReversiGameStream(this.gameId, 'message', { + publishReversiGameStream(this.gameId!, 'message', { userId: this.user.id, message }); @@ -121,29 +132,32 @@ export default class extends Channel { @autobind private async accept(accept: boolean) { - const game = await ReversiGames.findOne(this.gameId); + if (this.user == null) return; + + const game = await ReversiGames.findOne(this.gameId!); + if (game == null) throw 'game not found'; if (game.isStarted) return; let bothAccepted = false; if (game.user1Id === this.user.id) { - await ReversiGames.update({ id: this.gameId }, { + await ReversiGames.update(this.gameId!, { user1Accepted: accept }); - publishReversiGameStream(this.gameId, 'changeAccepts', { + publishReversiGameStream(this.gameId!, 'changeAccepts', { user1: accept, user2: game.user2Accepted }); if (accept && game.user2Accepted) bothAccepted = true; } else if (game.user2Id === this.user.id) { - await ReversiGames.update({ id: this.gameId }, { + await ReversiGames.update(this.gameId!, { user2Accepted: accept }); - publishReversiGameStream(this.gameId, 'changeAccepts', { + publishReversiGameStream(this.gameId!, 'changeAccepts', { user1: game.user1Accepted, user2: accept }); @@ -156,7 +170,7 @@ export default class extends Channel { if (bothAccepted) { // 3秒後、まだacceptされていたらゲーム開始 setTimeout(async () => { - const freshGame = await ReversiGames.findOne(this.gameId); + const freshGame = await ReversiGames.findOne(this.gameId!); if (freshGame == null || freshGame.isStarted || freshGame.isEnded) return; if (!freshGame.user1Accepted || !freshGame.user2Accepted) return; @@ -175,7 +189,7 @@ export default class extends Channel { const map = freshGame.map != null ? freshGame.map : getRandomMap(); - await ReversiGames.update({ id: this.gameId }, { + await ReversiGames.update(this.gameId!, { startedAt: new Date(), isStarted: true, black: bw, @@ -199,22 +213,20 @@ export default class extends Channel { winner = null; } - await ReversiGames.update({ - id: this.gameId - }, { + await ReversiGames.update(this.gameId!, { isEnded: true, winnerId: winner }); - publishReversiGameStream(this.gameId, 'ended', { + publishReversiGameStream(this.gameId!, 'ended', { winnerId: winner, - game: await ReversiGames.pack(this.gameId, this.user) + game: await ReversiGames.pack(this.gameId!, this.user) }); } //#endregion - publishReversiGameStream(this.gameId, 'started', - await ReversiGames.pack(this.gameId, this.user)); + publishReversiGameStream(this.gameId!, 'started', + await ReversiGames.pack(this.gameId!, this.user)); }, 3000); } } @@ -222,7 +234,10 @@ export default class extends Channel { // 石を打つ @autobind private async set(pos: number) { - const game = await ReversiGames.findOne(this.gameId); + if (this.user == null) return; + + const game = await ReversiGames.findOne(this.gameId!); + if (game == null) throw 'game not found'; if (!game.isStarted) return; if (game.isEnded) return; @@ -267,30 +282,29 @@ export default class extends Channel { game.logs.push(log); - await ReversiGames.update({ - id: this.gameId - }, { + await ReversiGames.update(this.gameId!, { crc32, isEnded: o.isEnded, winnerId: winner, logs: game.logs }); - publishReversiGameStream(this.gameId, 'set', Object.assign(log, { + publishReversiGameStream(this.gameId!, 'set', Object.assign(log, { next: o.turn })); if (o.isEnded) { - publishReversiGameStream(this.gameId, 'ended', { + publishReversiGameStream(this.gameId!, 'ended', { winnerId: winner, - game: await ReversiGames.pack(this.gameId, this.user) + game: await ReversiGames.pack(this.gameId!, this.user) }); } } @autobind private async check(crc32: string) { - const game = await ReversiGames.findOne(this.gameId); + const game = await ReversiGames.findOne(this.gameId!); + if (game == null) throw 'game not found'; if (!game.isStarted) return; diff --git a/src/server/api/stream/channels/games/reversi.ts b/src/server/api/stream/channels/games/reversi.ts index 0498e5e017..3db338386a 100644 --- a/src/server/api/stream/channels/games/reversi.ts +++ b/src/server/api/stream/channels/games/reversi.ts @@ -11,7 +11,7 @@ export default class extends Channel { @autobind public async init(params: any) { // Subscribe reversi stream - this.subscriber.on(`reversiStream:${this.user.id}`, data => { + this.subscriber.on(`reversiStream:${this.user!.id}`, data => { this.send(data); }); } @@ -22,7 +22,7 @@ export default class extends Channel { case 'ping': if (body.id == null) return; const matching = await ReversiMatchings.findOne({ - parentId: this.user.id, + parentId: this.user!.id, childId: body.id }); if (matching == null) return; diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts index 2cece0947f..61960657b4 100644 --- a/src/server/api/stream/channels/home-timeline.ts +++ b/src/server/api/stream/channels/home-timeline.ts @@ -17,10 +17,10 @@ export default class extends Channel { @autobind private async onNote(note: any) { // その投稿のユーザーをフォローしていなかったら弾く - if (this.user.id !== note.userId && !this.following.includes(note.userId)) return; + if (this.user!.id !== note.userId && !this.following.includes(note.userId)) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await Notes.pack(note.id, this.user, { + note = await Notes.pack(note.id, this.user!, { detail: true }); @@ -30,13 +30,13 @@ export default class extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user, { + note.reply = await Notes.pack(note.replyId, this.user!, { detail: true }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await Notes.pack(note.renoteId, this.user!, { detail: true }); } diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts index 30643aeda8..18e6aa8350 100644 --- a/src/server/api/stream/channels/hybrid-timeline.ts +++ b/src/server/api/stream/channels/hybrid-timeline.ts @@ -12,7 +12,7 @@ export default class extends Channel { @autobind public async init(params: any) { const meta = await fetchMeta(); - if (meta.disableLocalTimeline && !this.user.isAdmin && !this.user.isModerator) return; + if (meta.disableLocalTimeline && !this.user!.isAdmin && !this.user!.isModerator) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -22,13 +22,13 @@ export default class extends Channel { private async onNote(note: any) { // 自分自身の投稿 または その投稿のユーザーをフォローしている または ローカルの投稿 の場合だけ if (!( - this.user.id === note.userId || + this.user!.id === note.userId || this.following.includes(note.userId) || note.user.host == null )) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await Notes.pack(note.id, this.user, { + note = await Notes.pack(note.id, this.user!, { detail: true }); @@ -38,13 +38,13 @@ export default class extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user, { + note.reply = await Notes.pack(note.replyId, this.user!, { detail: true }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await Notes.pack(note.renoteId, this.user!, { detail: true }); } diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts index 0d9bf3149d..1100f87acb 100644 --- a/src/server/api/stream/channels/main.ts +++ b/src/server/api/stream/channels/main.ts @@ -9,10 +9,10 @@ export default class extends Channel { @autobind public async init(params: any) { - const mute = await Mutings.find({ muterId: this.user.id }); + const mute = await Mutings.find({ muterId: this.user!.id }); // Subscribe main stream channel - this.subscriber.on(`mainStream:${this.user.id}`, async data => { + this.subscriber.on(`mainStream:${this.user!.id}`, async data => { const { type, body } = data; switch (type) { diff --git a/src/server/api/stream/channels/messaging-index.ts b/src/server/api/stream/channels/messaging-index.ts index 648badc1dc..0c495398ab 100644 --- a/src/server/api/stream/channels/messaging-index.ts +++ b/src/server/api/stream/channels/messaging-index.ts @@ -9,7 +9,7 @@ export default class extends Channel { @autobind public async init(params: any) { // Subscribe messaging index stream - this.subscriber.on(`messagingIndexStream:${this.user.id}`, data => { + this.subscriber.on(`messagingIndexStream:${this.user!.id}`, data => { this.send(data); }); } diff --git a/src/server/api/stream/channels/messaging.ts b/src/server/api/stream/channels/messaging.ts index b81fbb9d4c..8397f849ff 100644 --- a/src/server/api/stream/channels/messaging.ts +++ b/src/server/api/stream/channels/messaging.ts @@ -14,7 +14,7 @@ export default class extends Channel { this.otherpartyId = params.otherparty as string; // Subscribe messaging stream - this.subscriber.on(`messagingStream:${this.user.id}-${this.otherpartyId}`, data => { + this.subscriber.on(`messagingStream:${this.user!.id}-${this.otherpartyId}`, data => { this.send(data); }); } @@ -23,7 +23,7 @@ export default class extends Channel { public onMessage(type: string, body: any) { switch (type) { case 'read': - read(this.user.id, this.otherpartyId, body.id); + read(this.user!.id, this.otherpartyId, body.id); break; } } diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts index abbd91ec81..f73f3229d5 100644 --- a/src/server/api/stream/index.ts +++ b/src/server/api/stream/index.ts @@ -28,13 +28,13 @@ export default class Connection { constructor( wsConnection: websocket.connection, subscriber: EventEmitter, - user: User, - app: App + user: User | null | undefined, + app: App | null | undefined ) { this.wsConnection = wsConnection; - this.user = user; - this.app = app; this.subscriber = subscriber; + if (user) this.user = user; + if (app) this.app = app; this.wsConnection.on('message', this.onWsConnectionMessage); @@ -52,6 +52,8 @@ export default class Connection { */ @autobind private async onWsConnectionMessage(data: websocket.IMessage) { + if (data.utf8Data == null) return; + const { type, body } = JSON.parse(data.utf8Data); switch (type) { @@ -89,7 +91,7 @@ export default class Connection { @autobind private onReadNotification(payload: any) { if (!payload.id) return; - readNotification(this.user.id, [payload.id]); + readNotification(this.user!.id, [payload.id]); } /** @@ -109,7 +111,7 @@ export default class Connection { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } - if (payload.read) { + if (payload.read && this.user) { readNote(this.user.id, payload.id); } } @@ -221,7 +223,7 @@ export default class Connection { private async updateFollowing() { const followings = await Followings.find({ where: { - followerId: this.user.id + followerId: this.user!.id }, select: ['followeeId'] }); @@ -233,7 +235,7 @@ export default class Connection { private async updateMuting() { const mutings = await Mutings.find({ where: { - muterId: this.user.id + muterId: this.user!.id }, select: ['muteeId'] }); @@ -247,7 +249,7 @@ export default class Connection { @autobind public dispose() { for (const c of this.channels.filter(c => c.dispose)) { - c.dispose(); + if (c.dispose) c.dispose(); } if (this.followingClock) clearInterval(this.followingClock); diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts index f9b067b79c..5da3d79eb5 100644 --- a/src/server/file/send-drive-file.ts +++ b/src/server/file/send-drive-file.ts @@ -50,7 +50,7 @@ export default async function(ctx: Koa.BaseContext) { ctx.set('Content-Disposition', contentDisposition('attachment', `${file.name}`)); } - const readable = InternalStorage.read(file.accessKey); + const readable = InternalStorage.read(file.accessKey!); readable.on('error', commonReadableHandlerGenerator(ctx)); ctx.set('Content-Type', file.type); ctx.body = readable; diff --git a/src/server/index.ts b/src/server/index.ts index 9c153f0167..601e288f3b 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -32,7 +32,7 @@ export const serverLogger = new Logger('server', 'gray', false); const app = new Koa(); app.proxy = true; -if (!['production', 'test'].includes(process.env.NODE_ENV)) { +if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { // Logger app.use(koaLogger(str => { serverLogger.info(str); diff --git a/src/server/web/docs.ts b/src/server/web/docs.ts index d9ba14a8ed..374dbf3bd2 100644 --- a/src/server/web/docs.ts +++ b/src/server/web/docs.ts @@ -34,14 +34,14 @@ async function genVars(lang: string): Promise<{ [key: string]: any }> { const docs = glob.sync(`src/docs/**/*.${lang}.md`, { cwd }); vars['docs'] = {}; for (const x of docs) { - const [, name] = x.match(/docs\/(.+?)\.(.+?)\.md$/); + const [, name] = x.match(/docs\/(.+?)\.(.+?)\.md$/)!; if (vars['docs'][name] == null) { vars['docs'][name] = { name, title: {} }; } - vars['docs'][name]['title'][lang] = fs.readFileSync(cwd + x, 'utf-8').match(/^# (.+?)\r?\n/)[1]; + vars['docs'][name]['title'][lang] = fs.readFileSync(cwd + x, 'utf-8').match(/^# (.+?)\r?\n/)![1]; } vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(); @@ -97,7 +97,7 @@ router.get('/*/*', async ctx => { await ctx.render('../../../../src/docs/article', Object.assign({ id: doc, html: conv.makeHtml(md), - title: md.match(/^# (.+?)\r?\n/)[1], + title: md.match(/^# (.+?)\r?\n/)![1], src: `https://github.com/syuilo/misskey/tree/master/src/docs/${doc}.${lang}.md` }, await genVars(lang))); diff --git a/src/server/web/feed.ts b/src/server/web/feed.ts index 94f0643f75..285c20fcdb 100644 --- a/src/server/web/feed.ts +++ b/src/server/web/feed.ts @@ -3,6 +3,7 @@ import config from '../../config'; import { User } from '../../models/entities/user'; import { Notes, DriveFiles, UserProfiles } from '../../models'; import { In } from 'typeorm'; +import { ensure } from '../../prelude/ensure'; export default async function(user: User) { const author: Author = { @@ -10,7 +11,7 @@ export default async function(user: User) { name: user.name || user.username }; - const profile = await UserProfiles.findOne({ userId: user.id }); + const profile = await UserProfiles.findOne({ userId: user.id }).then(ensure); const notes = await Notes.find({ where: { @@ -47,9 +48,9 @@ export default async function(user: User) { title: `New note by ${author.name}`, link: `${config.url}/notes/${note.id}`, date: note.createdAt, - description: note.cw, - content: note.text, - image: file ? DriveFiles.getPublicUrl(file) : null + description: note.cw || undefined, + content: note.text || undefined, + image: file ? DriveFiles.getPublicUrl(file) : undefined }); } diff --git a/src/server/web/index.ts b/src/server/web/index.ts index d1a15e3929..5cadf1b124 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -19,6 +19,7 @@ import config from '../../config'; import { Users, Notes, Emojis, UserProfiles } from '../../models'; import parseAcct from '../../misc/acct/parse'; import getNoteSummary from '../../misc/get-note-summary'; +import { ensure } from '../../prelude/ensure'; const client = `${__dirname}/../../client/`; @@ -149,11 +150,9 @@ router.get('/@:user', async (ctx, next) => { usernameLower: username.toLowerCase(), host }); - const profile = await UserProfiles.findOne({ - userId: user.id - }); if (user != null) { + const profile = await UserProfiles.findOne(user.id).then(ensure); const meta = await fetchMeta(); await ctx.render('user', { user, profile, diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts index aed475e6ff..7d0080b4d2 100644 --- a/src/server/web/url-preview.ts +++ b/src/server/web/url-preview.ts @@ -43,7 +43,7 @@ module.exports = async (ctx: Koa.BaseContext) => { } }; -function wrap(url: string): string { +function wrap(url?: string): string | null { return url != null ? url.match(/^https?:\/\//) ? `${config.url}/proxy/preview.jpg?${query({ diff --git a/src/server/well-known.ts b/src/server/well-known.ts index 7c5684d2ce..d29b7a8888 100644 --- a/src/server/well-known.ts +++ b/src/server/well-known.ts @@ -54,9 +54,9 @@ router.get(webFingerPath, async ctx => { const generateQuery = (resource: string) => resource.startsWith(`${config.url.toLowerCase()}/users/`) ? - fromId(resource.split('/').pop()) : + fromId(resource.split('/').pop()!) : fromAcct(parseAcct( - resource.startsWith(`${config.url.toLowerCase()}/@`) ? resource.split('/').pop() : + resource.startsWith(`${config.url.toLowerCase()}/@`) ? resource.split('/').pop()! : resource.startsWith('acct:') ? resource.slice('acct:'.length) : resource)); diff --git a/src/services/blocking/create.ts b/src/services/blocking/create.ts index 79ca0d59f1..def4f33585 100644 --- a/src/services/blocking/create.ts +++ b/src/services/blocking/create.ts @@ -66,7 +66,7 @@ async function cancelRequest(follower: User, followee: User) { // リモートからフォローリクエストを受けていたらReject送信 if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId), followee)); + const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId!), followee)); deliver(followee, content, follower.inbox); } } diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index 2a60b1a0a3..0a9ec8dae0 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -84,7 +84,7 @@ export default abstract class Chart> { } } }; - flatColumns(schema.properties); + flatColumns(schema.properties!); return columns; } @@ -182,7 +182,7 @@ export default abstract class Chart> { } @autobind - private getNewLog(latest?: T): T { + private getNewLog(latest: T | null): T { const log = latest ? this.genNewLog(latest) : {}; const flatColumns = (x: Obj, path?: string) => { for (const [k, v] of Object.entries(x)) { @@ -196,7 +196,7 @@ export default abstract class Chart> { } } }; - flatColumns(this.schema.properties); + flatColumns(this.schema.properties!); return log as T; } @@ -213,7 +213,7 @@ export default abstract class Chart> { } @autobind - private getLatestLog(span: Span, group: string = null): Promise { + private getLatestLog(span: Span, group: string | null = null): Promise { return this.repository.findOne({ group: group, span: span @@ -221,17 +221,17 @@ export default abstract class Chart> { order: { date: -1 } - }); + }).then(x => x || null); } @autobind - private async getCurrentLog(span: Span, group: string = null): Promise { + private async getCurrentLog(span: Span, group: string | null = null): Promise { const [y, m, d, h] = this.getCurrentDate(); const current = span == 'day' ? utc([y, m, d]) : span == 'hour' ? utc([y, m, d, h]) : - null; + null as never; // 現在(今日または今のHour)のログ const currentLog = await this.repository.findOne({ @@ -285,7 +285,7 @@ export default abstract class Chart> { // 並列動作している他のチャートエンジンプロセスと処理が重なる場合がある // その場合は再度最も新しいログを持ってくる if (isDuplicateKeyValueError(e)) { - log = await this.getLatestLog(span, group); + log = await this.getLatestLog(span, group) as Log; } else { logger.error(e); throw e; @@ -296,17 +296,17 @@ export default abstract class Chart> { } @autobind - protected commit(query: Record, group: string = null, uniqueKey?: string, uniqueValue?: string): Promise { + protected commit(query: Record, group: string | null = null, uniqueKey?: string, uniqueValue?: string): Promise { const update = async (log: Log) => { // ユニークインクリメントの場合、指定のキーに指定の値が既に存在していたら弾く if ( - uniqueKey && + uniqueKey && log.unique && log.unique[uniqueKey] && log.unique[uniqueKey].includes(uniqueValue) ) return; // ユニークインクリメントの指定のキーに値を追加 - if (uniqueKey) { + if (uniqueKey && log.unique) { if (log.unique[uniqueKey]) { const sql = `jsonb_set("unique", '{${uniqueKey}}', ("unique"->>'${uniqueKey}')::jsonb || '["${uniqueValue}"]'::jsonb)`; query['unique'] = () => sql; @@ -331,23 +331,23 @@ export default abstract class Chart> { } @autobind - protected async inc(inc: DeepPartial, group: string = null): Promise { + protected async inc(inc: DeepPartial, group: string | null = null): Promise { await this.commit(Chart.convertQuery(inc as any), group); } @autobind - protected async incIfUnique(inc: DeepPartial, key: string, value: string, group: string = null): Promise { + protected async incIfUnique(inc: DeepPartial, key: string, value: string, group: string | null = null): Promise { await this.commit(Chart.convertQuery(inc as any), group, key, value); } @autobind - public async getChart(span: Span, range: number, group: string = null): Promise> { + public async getChart(span: Span, range: number, group: string | null = null): Promise> { const [y, m, d, h] = this.getCurrentDate(); const gt = span == 'day' ? utc([y, m, d]).subtract(range, 'days') : span == 'hour' ? utc([y, m, d, h]).subtract(range, 'hours') : - null; + null as never; // ログ取得 let logs = await this.repository.find({ @@ -404,7 +404,7 @@ export default abstract class Chart> { const current = span == 'day' ? utc([y, m, d]).subtract(i, 'days') : span == 'hour' ? utc([y, m, d, h]).subtract(i, 'hours') : - null; + null as never; const log = logs.find(l => utc(l.date * 1000).isSame(current)); @@ -452,8 +452,8 @@ export function convertLog(logSchema: Schema): Schema { type: 'number' }; } else if (v.type === 'object') { - for (const k of Object.keys(v.properties)) { - v.properties[k] = convertLog(v.properties[k]); + for (const k of Object.keys(v.properties!)) { + v.properties![k] = convertLog(v.properties![k]); } } return v; diff --git a/src/services/create-notification.ts b/src/services/create-notification.ts index bcb8214c56..5bff8adfd4 100644 --- a/src/services/create-notification.ts +++ b/src/services/create-notification.ts @@ -46,6 +46,7 @@ export async function createNotification( // 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する setTimeout(async () => { const fresh = await Notifications.findOne(notification.id); + if (fresh == null) return; // 既に削除されているかもしれない if (!fresh.isRead) { //#region ただしミュートしているユーザーからの通知なら無視 const mutings = await Mutings.find({ diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts index b83c3558d3..94b97fed61 100644 --- a/src/services/drive/add-file.ts +++ b/src/services/drive/add-file.ts @@ -55,10 +55,10 @@ async function save(file: DriveFile, path: string, name: string, type: string, h const url = `${ baseUrl }/${ key }`; // for alts - let webpublicKey: string = null; - let webpublicUrl: string = null; - let thumbnailKey: string = null; - let thumbnailUrl: string = null; + let webpublicKey: string | null = null; + let webpublicUrl: string | null = null; + let thumbnailKey: string | null = null; + let thumbnailUrl: string | null = null; //#endregion //#region Uploads @@ -106,8 +106,8 @@ async function save(file: DriveFile, path: string, name: string, type: string, h const url = InternalStorage.saveFromPath(accessKey, path); - let thumbnailUrl: string; - let webpublicUrl: string; + let thumbnailUrl: string | null = null; + let webpublicUrl: string | null = null; if (alts.thumbnail) { thumbnailUrl = InternalStorage.saveFromBuffer(thumbnailAccessKey, alts.thumbnail.data); @@ -143,7 +143,7 @@ async function save(file: DriveFile, path: string, name: string, type: string, h */ export async function generateAlts(path: string, type: string, generateWeb: boolean) { // #region webpublic - let webpublic: IImage; + let webpublic: IImage | null = null; if (generateWeb) { logger.info(`creating web image`); @@ -163,7 +163,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool // #endregion webpublic // #region thumbnail - let thumbnail: IImage; + let thumbnail: IImage | null = null; if (['image/jpeg', 'image/webp'].includes(type)) { thumbnail = await ConvertToJpeg(path, 498, 280); @@ -188,7 +188,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool * Upload to ObjectStorage */ async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { - const minio = new Minio.Client(config.drive.config); + const minio = new Minio.Client(config.drive!.config); const metadata = { 'Content-Type': type, @@ -197,7 +197,7 @@ async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, if (filename) metadata['Content-Disposition'] = contentDisposition('inline', filename); - await minio.putObject(config.drive.bucket, key, stream, null, metadata); + await minio.putObject(config.drive!.bucket!, key, stream, undefined, metadata); } async function deleteOldFile(user: IRemoteUser) { @@ -232,14 +232,14 @@ async function deleteOldFile(user: IRemoteUser) { export default async function( user: User, path: string, - name: string = null, - comment: string = null, + name: string | null = null, + comment: string | null = null, folderId: any = null, force: boolean = false, isLink: boolean = false, - url: string = null, - uri: string = null, - sensitive: boolean = null + url: string | null = null, + uri: string | null = null, + sensitive: boolean | null = null ): Promise { // Calc md5 hash const calcHash = new Promise((res, rej) => { @@ -300,7 +300,7 @@ export default async function( throw 'no-free-space'; } else { // (アバターまたはバナーを含まず)最も古いファイルを削除する - deleteOldFile(user); + deleteOldFile(user as IRemoteUser); } } } @@ -378,7 +378,7 @@ export default async function( file.comment = comment; file.properties = properties; file.isLink = isLink; - file.isSensitive = Users.isLocalUser(user) && profile.alwaysMarkNsfw ? true : + file.isSensitive = Users.isLocalUser(user) && profile!.alwaysMarkNsfw ? true : (sensitive !== null && sensitive !== undefined) ? sensitive : false; @@ -411,7 +411,7 @@ export default async function( file = await DriveFiles.findOne({ uri: file.uri, userId: user.id - }); + }) as DriveFile; } else { logger.error(e); throw e; diff --git a/src/services/drive/delete-file.ts b/src/services/drive/delete-file.ts index b7fa99391e..bba453b982 100644 --- a/src/services/drive/delete-file.ts +++ b/src/services/drive/delete-file.ts @@ -7,31 +7,31 @@ import { driveChart, perUserDriveChart, instanceChart } from '../chart'; export default async function(file: DriveFile, isExpired = false) { if (file.storedInternal) { - InternalStorage.del(file.accessKey); + InternalStorage.del(file.accessKey!); if (file.thumbnailUrl) { - InternalStorage.del(file.thumbnailAccessKey); + InternalStorage.del(file.thumbnailAccessKey!); } if (file.webpublicUrl) { - InternalStorage.del(file.webpublicAccessKey); + InternalStorage.del(file.webpublicAccessKey!); } } else if (!file.isLink) { - const minio = new Minio.Client(config.drive.config); + const minio = new Minio.Client(config.drive!.config); - await minio.removeObject(config.drive.bucket, file.accessKey); + await minio.removeObject(config.drive!.bucket!, file.accessKey!); if (file.thumbnailUrl) { - await minio.removeObject(config.drive.bucket, file.thumbnailAccessKey); + await minio.removeObject(config.drive!.bucket!, file.thumbnailAccessKey!); } if (file.webpublicUrl) { - await minio.removeObject(config.drive.bucket, file.webpublicAccessKey); + await minio.removeObject(config.drive!.bucket!, file.webpublicAccessKey!); } } // リモートファイル期限切れ削除後は直リンクにする - if (isExpired && file.userHost !== null) { + if (isExpired && file.userHost !== null && file.uri != null) { DriveFiles.update(file.id, { isLink: true, url: file.uri, diff --git a/src/services/drive/image-processor.ts b/src/services/drive/image-processor.ts index 3c538390b0..89ac331ca1 100644 --- a/src/services/drive/image-processor.ts +++ b/src/services/drive/image-processor.ts @@ -2,7 +2,7 @@ import * as sharp from 'sharp'; export type IImage = { data: Buffer; - ext: string; + ext: string | null; type: string; }; diff --git a/src/services/drive/upload-from-url.ts b/src/services/drive/upload-from-url.ts index a7fe1fbd26..bfe8c5c3b2 100644 --- a/src/services/drive/upload-from-url.ts +++ b/src/services/drive/upload-from-url.ts @@ -1,4 +1,3 @@ -import * as URL from 'url'; import create from './add-file'; import { User } from '../../models/entities/user'; import { driveLogger } from './logger'; @@ -13,14 +12,14 @@ const logger = driveLogger.createSubLogger('downloader'); export default async ( url: string, user: User, - folderId: DriveFolder['id'] = null, - uri: string = null, + folderId: DriveFolder['id'] | null = null, + uri: string | null = null, sensitive = false, force = false, link = false ): Promise => { - let name = URL.parse(url).pathname.split('/').pop(); - if (!DriveFiles.validateFileName(name)) { + let name = new URL(url).pathname.split('/').pop() || null; + if (name == null || !DriveFiles.validateFileName(name)) { name = null; } @@ -50,6 +49,6 @@ export default async ( if (error) { throw error; } else { - return driveFile; + return driveFile!; } }; diff --git a/src/services/following/create.ts b/src/services/following/create.ts index 57bb61fd92..b69dfe42b9 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -14,6 +14,7 @@ import { instanceChart, perUserFollowingChart } from '../chart'; import { genId } from '../../misc/gen-id'; import { createNotification } from '../create-notification'; import { isDuplicateKeyValueError } from '../../misc/is-duplicate-key-value-error'; +import { ensure } from '../../prelude/ensure'; const logger = new Logger('following/create'); @@ -115,7 +116,7 @@ export default async function(follower: User, followee: User, requestId?: string if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); } - const followeeProfile = await UserProfiles.findOne({ userId: followee.id }); + const followeeProfile = await UserProfiles.findOne(followee.id).then(ensure); // フォロー対象が鍵アカウントである or // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or diff --git a/src/services/following/requests/accept-all.ts b/src/services/following/requests/accept-all.ts index b61c31a513..70e7448aad 100644 --- a/src/services/following/requests/accept-all.ts +++ b/src/services/following/requests/accept-all.ts @@ -1,6 +1,7 @@ import accept from './accept'; import { User } from '../../../models/entities/user'; import { FollowRequests, Users } from '../../../models'; +import { ensure } from '../../../prelude/ensure'; /** * 指定したユーザー宛てのフォローリクエストをすべて承認 @@ -12,7 +13,7 @@ export default async function(user: User) { }); for (const request of requests) { - const follower = await Users.findOne(request.followerId); + const follower = await Users.findOne(request.followerId).then(ensure); accept(user, follower); } } diff --git a/src/services/following/requests/accept.ts b/src/services/following/requests/accept.ts index 0be8e24e1a..1397514ad1 100644 --- a/src/services/following/requests/accept.ts +++ b/src/services/following/requests/accept.ts @@ -16,7 +16,7 @@ export default async function(followee: User, follower: User) { await insertFollowingDoc(followee, follower); if (Users.isRemoteUser(follower) && request) { - const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId), followee as ILocalUser)); + const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId!), followee as ILocalUser)); deliver(followee as ILocalUser, content, follower.inbox); } diff --git a/src/services/following/requests/reject.ts b/src/services/following/requests/reject.ts index c590edcfd8..d5b5e48c41 100644 --- a/src/services/following/requests/reject.ts +++ b/src/services/following/requests/reject.ts @@ -13,7 +13,7 @@ export default async function(followee: User, follower: User) { followerId: follower.id }); - const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId), followee as ILocalUser)); + const content = renderActivity(renderReject(renderFollow(follower, followee, request!.requestId!), followee as ILocalUser)); deliver(followee as ILocalUser, content, follower.inbox); } diff --git a/src/services/i/pin.ts b/src/services/i/pin.ts index 4e43421bdc..f5c957c742 100644 --- a/src/services/i/pin.ts +++ b/src/services/i/pin.ts @@ -77,9 +77,8 @@ export async function removePinned(user: User, noteId: Note['id']) { } export async function deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { - const user = await Users.findOne({ - id: userId - }); + const user = await Users.findOne(userId); + if (user == null) throw 'user not found'; if (!Users.isLocalUser(user)) return; @@ -108,14 +107,8 @@ async function CreateRemoteInboxes(user: ILocalUser): Promise { const queue: string[] = []; for (const following of followers) { - const follower = { - host: following.followerHost, - inbox: following.followerInbox, - sharedInbox: following.followerSharedInbox, - }; - - if (follower.host !== null) { - const inbox = follower.sharedInbox || follower.inbox; + if (Followings.isRemoteFollower(following)) { + const inbox = following.followerSharedInbox || following.followerInbox; if (!queue.includes(inbox)) queue.push(inbox); } } diff --git a/src/services/i/update.ts b/src/services/i/update.ts index 7dba472e78..2bb5c7a100 100644 --- a/src/services/i/update.ts +++ b/src/services/i/update.ts @@ -6,9 +6,8 @@ import { User } from '../../models/entities/user'; import { renderPerson } from '../../remote/activitypub/renderer/person'; export async function publishToFollowers(userId: User['id']) { - const user = await Users.findOne({ - id: userId - }); + const user = await Users.findOne(userId); + if (user == null) throw 'user not found'; const followers = await Followings.find({ followeeId: user.id @@ -19,7 +18,7 @@ export async function publishToFollowers(userId: User['id']) { // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 if (Users.isLocalUser(user)) { for (const following of followers) { - if (following.followerHost !== null) { + if (Followings.isRemoteFollower(following)) { const inbox = following.followerSharedInbox || following.followerInbox; if (!queue.includes(inbox)) queue.push(inbox); } diff --git a/src/services/logger.ts b/src/services/logger.ts index e6a54e626d..190bbdd253 100644 --- a/src/services/logger.ts +++ b/src/services/logger.ts @@ -9,14 +9,14 @@ import { genId } from '../misc/gen-id'; type Domain = { name: string; - color: string; + color?: string; }; type Level = 'error' | 'success' | 'warning' | 'debug' | 'info'; export default class Logger { private domain: Domain; - private parentLogger: Logger; + private parentLogger: Logger | null = null; private store: boolean; constructor(domain: string, color?: string, store = true) { @@ -33,7 +33,7 @@ export default class Logger { return logger; } - private log(level: Level, message: string, data: Record, important = false, subDomains: Domain[] = [], store = true): void { + private log(level: Level, message: string, data?: Record | null, important = false, subDomains: Domain[] = [], store = true): void { if (program.quiet) return; if (!this.store) store = false; @@ -80,7 +80,7 @@ export default class Logger { } } - public error(x: string | Error, data?: Record, important = false): void { // 実行を継続できない状況で使う + public error(x: string | Error, data?: Record | null, important = false): void { // 実行を継続できない状況で使う if (x instanceof Error) { data = data || {}; data.e = x; @@ -90,21 +90,21 @@ export default class Logger { } } - public warn(message: string, data?: Record, important = false): void { // 実行を継続できるが改善すべき状況で使う + public warn(message: string, data?: Record | null, important = false): void { // 実行を継続できるが改善すべき状況で使う this.log('warning', message, data, important); } - public succ(message: string, data?: Record, important = false): void { // 何かに成功した状況で使う + public succ(message: string, data?: Record | null, important = false): void { // 何かに成功した状況で使う this.log('success', message, data, important); } - public debug(message: string, data?: Record, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) + public debug(message: string, data?: Record | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) if (process.env.NODE_ENV != 'production' || program.verbose) { this.log('debug', message, data, important); } } - public info(message: string, data?: Record, important = false): void { // それ以外 + public info(message: string, data?: Record | null, important = false): void { // それ以外 this.log('info', message, data, important); } } diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 6906bc71ce..9c5989696a 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -10,7 +10,7 @@ import { parse } from '../../mfm/parse'; import { resolveUser } from '../../remote/resolve-user'; import config from '../../config'; import { updateHashtag } from '../update-hashtag'; -import { erase, concat } from '../../prelude/array'; +import { concat } from '../../prelude/array'; import insertNoteUnread from './unread'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc'; import extractMentions from '../../misc/extract-mentions'; @@ -27,6 +27,7 @@ import { notesChart, perUserNotesChart, activeUsersChart, instanceChart } from ' import { Poll, IPoll } from '../../models/entities/poll'; import { createNotification } from '../create-notification'; import { isDuplicateKeyValueError } from '../../misc/is-duplicate-key-value-error'; +import { ensure } from '../../prelude/ensure'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -83,25 +84,25 @@ class NotificationManager { } type Option = { - createdAt?: Date; - name?: string; - text?: string; - reply?: Note; - renote?: Note; - files?: DriveFile[]; - geo?: any; - poll?: IPoll; - viaMobile?: boolean; - localOnly?: boolean; - cw?: string; + createdAt?: Date | null; + name?: string | null; + text?: string | null; + reply?: Note | null; + renote?: Note | null; + files?: DriveFile[] | null; + geo?: any | null; + poll?: IPoll | null; + viaMobile?: boolean | null; + localOnly?: boolean | null; + cw?: string | null; visibility?: string; - visibleUsers?: User[]; - apMentions?: User[]; - apHashtags?: string[]; - apEmojis?: string[]; - questionUri?: string; - uri?: string; - app?: App; + visibleUsers?: User[] | null; + apMentions?: User[] | null; + apHashtags?: string[] | null; + apEmojis?: string[] | null; + questionUri?: string | null; + uri?: string | null; + app?: App | null; }; export default async (user: User, data: Option, silent = false) => new Promise(async (res, rej) => { @@ -117,10 +118,6 @@ export default async (user: User, data: Option, silent = false) => new Promise new Promise parse(choice))) + ? concat(data.poll.choices.map(choice => parse(choice)!)) : []; const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); @@ -173,19 +170,21 @@ export default async (user: User, data: Option, silent = false) => new Promise tag.length <= 100); - if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply.userId)) { - mentionedUsers.push(await Users.findOne(data.reply.userId)); + if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { + mentionedUsers.push(await Users.findOne(data.reply.userId).then(ensure)); } if (data.visibility == 'specified') { + if (data.visibleUsers == null) throw 'invalid param'; + for (const u of data.visibleUsers) { if (!mentionedUsers.some(x => x.id === u.id)) { mentionedUsers.push(u); } } - if (data.reply && !data.visibleUsers.some(x => x.id === data.reply.userId)) { - data.visibleUsers.push(await Users.findOne(data.reply.userId)); + if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) { + data.visibleUsers.push(await Users.findOne(data.reply.userId).then(ensure)); } } @@ -215,6 +214,8 @@ export default async (user: User, data: Option, silent = false) => new Promise new Promise { + Users.findOne(reply.userId).then(ensure).then(u => { deliver(user, noteActivity, u.inbox); }); } // 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送 if (renote && renote.userHost !== null) { - Users.findOne(renote.userId).then(u => { + Users.findOne(renote.userId).then(ensure).then(u => { deliver(user, noteActivity, u.inbox); }); } @@ -340,14 +341,14 @@ async function publish(user: User, note: Note, reply: Note, renote: Note, noteAc if (['public', 'home', 'followers'].includes(note.visibility)) { // フォロワーに配信 - publishToFollowers(note, user, noteActivity, reply); + publishToFollowers(note, user, noteActivity); } } async function insertNote(user: User, data: Option, tags: string[], emojis: string[], mentionedUsers: User[]) { const insert = new Note({ - id: genId(data.createdAt), - createdAt: data.createdAt, + id: genId(data.createdAt!), + createdAt: data.createdAt!, fileIds: data.files ? data.files.map(file => file.id) : [], replyId: data.reply ? data.reply.id : null, renoteId: data.renote ? data.renote.id : null, @@ -358,8 +359,8 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri tags: tags.map(tag => tag.toLowerCase()), emojis, userId: user.id, - viaMobile: data.viaMobile, - localOnly: data.localOnly, + viaMobile: data.viaMobile!, + localOnly: data.localOnly!, geo: data.geo || null, appId: data.app ? data.app.id : null, visibility: data.visibility as any, @@ -401,10 +402,10 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri const poll = new Poll({ noteId: note.id, - choices: data.poll.choices, - expiresAt: data.poll.expiresAt, - multiple: data.poll.multiple, - votes: new Array(data.poll.choices.length).fill(0), + choices: data.poll!.choices, + expiresAt: data.poll!.expiresAt, + multiple: data.poll!.multiple, + votes: new Array(data.poll!.choices.length).fill(0), noteVisibility: note.visibility, userId: user.id, userHost: user.host @@ -416,7 +417,7 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri note = await Notes.save(insert); } - return note; + return note!; } catch (e) { // duplicate key error if (isDuplicateKeyValueError(e)) { @@ -434,7 +435,7 @@ async function insertNote(user: User, data: Option, tags: string[], emojis: stri function index(note: Note) { if (note.text == null || config.elasticsearch == null) return; - es.index({ + es!.index({ index: 'misskey', type: 'note', id: note.id.toString(), @@ -466,7 +467,7 @@ async function notifyToWatchersOfReplyee(reply: Note, user: User, nm: Notificati } } -async function publishToFollowers(note: Note, user: User, noteActivity: any, reply: Note) { +async function publishToFollowers(note: Note, user: User, noteActivity: any) { const followers = await Followings.find({ followeeId: note.userId }); @@ -474,7 +475,7 @@ async function publishToFollowers(note: Note, user: User, noteActivity: any, rep const queue: string[] = []; for (const following of followers) { - if (following.followerHost !== null) { + if (Followings.isRemoteFollower(following)) { // フォロワーがリモートユーザーかつ投稿者がローカルユーザーなら投稿を配信 if (Users.isLocalUser(user)) { const inbox = following.followerSharedInbox || following.followerInbox; @@ -523,11 +524,9 @@ async function extractMentionedUsers(user: User, tokens: ReturnType + let mentionedUsers = (await Promise.all(mentions.map(m => resolveUser(m.username, m.host || user.host).catch(() => null) - )); - - mentionedUsers = mentionedUsers.filter(x => x != null); + ))).filter(x => x != null) as User[]; // Drop duplicate users mentionedUsers = mentionedUsers.filter((u, i, self) => diff --git a/src/services/note/polls/update.ts b/src/services/note/polls/update.ts index beb35cc278..277ace204d 100644 --- a/src/services/note/polls/update.ts +++ b/src/services/note/polls/update.ts @@ -7,8 +7,10 @@ import { Note } from '../../../models/entities/note'; export async function deliverQuestionUpdate(noteId: Note['id']) { const note = await Notes.findOne(noteId); + if (note == null) throw 'note not found'; const user = await Users.findOne(note.userId); + if (user == null) throw 'note not found'; const followers = await Followings.find({ followeeId: user.id @@ -19,13 +21,8 @@ export async function deliverQuestionUpdate(noteId: Note['id']) { // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 if (Users.isLocalUser(user)) { for (const following of followers) { - const follower = { - inbox: following.followerInbox, - sharedInbox: following.followerSharedInbox - }; - - if (following.followerHost !== null) { - const inbox = follower.sharedInbox || follower.inbox; + if (Followings.isRemoteFollower(following)) { + const inbox = following.followerSharedInbox || following.followerInbox; if (!queue.includes(inbox)) queue.push(inbox); } } diff --git a/src/services/note/polls/vote.ts b/src/services/note/polls/vote.ts index 3051ff42d9..3bae617b4f 100644 --- a/src/services/note/polls/vote.ts +++ b/src/services/note/polls/vote.ts @@ -7,11 +7,13 @@ import { Not } from 'typeorm'; import { genId } from '../../../misc/gen-id'; import { createNotification } from '../../create-notification'; -export default (user: User, note: Note, choice: number) => new Promise(async (res, rej) => { +export default async function(user: User, note: Note, choice: number) { const poll = await Polls.findOne({ noteId: note.id }); + if (poll == null) throw 'poll not found'; + // Check whether is valid choice - if (poll.choices[choice] == null) return rej('invalid choice param'); + if (poll.choices[choice] == null) throw new Error('invalid choice param'); // if already voted const exist = await PollVotes.find({ @@ -21,10 +23,10 @@ export default (user: User, note: Note, choice: number) => new Promise(async (re if (poll.multiple) { if (exist.some(x => x.choice === choice)) { - return rej('already voted'); + throw new Error('already voted'); } } else if (exist.length !== 0) { - return rej('already voted'); + throw new Error('already voted'); } // Create vote @@ -36,8 +38,6 @@ export default (user: User, note: Note, choice: number) => new Promise(async (re choice: choice }); - res(); - // Increment votes count const index = choice + 1; // In SQL, array index is 1 based await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE noteId = '${poll.noteId}'`); @@ -70,7 +70,7 @@ export default (user: User, note: Note, choice: number) => new Promise(async (re const profile = await UserProfiles.findOne({ userId: user.id }); // ローカルユーザーが投票した場合この投稿をWatchする - if (Users.isLocalUser(user) && profile.autoWatch) { + if (Users.isLocalUser(user) && profile!.autoWatch) { watch(user.id, note); } -}); +} diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index 55f9bcd084..ea2108430a 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -82,7 +82,7 @@ export default async (user: User, note: Note, reaction: string) => { const profile = await UserProfiles.findOne({ userId: user.id }); // ユーザーがローカルユーザーかつ自動ウォッチ設定がオンならばこの投稿をWatchする - if (Users.isLocalUser(user) && profile.autoWatch) { + if (Users.isLocalUser(user) && profile!.autoWatch) { watch(user.id, note); } @@ -91,7 +91,7 @@ export default async (user: User, note: Note, reaction: string) => { if (Users.isLocalUser(user) && note.userHost !== null) { const content = renderActivity(renderLike(user, note, reaction)); Users.findOne(note.userId).then(u => { - deliver(user, content, u.inbox); + deliver(user, content, u!.inbox); }); } //#endregion diff --git a/src/services/note/reaction/delete.ts b/src/services/note/reaction/delete.ts index 76f5ed631e..355e1c5466 100644 --- a/src/services/note/reaction/delete.ts +++ b/src/services/note/reaction/delete.ts @@ -42,7 +42,7 @@ export default async (user: User, note: Note) => { if (Users.isLocalUser(user) && (note.userHost !== null)) { const content = renderActivity(renderUndo(renderLike(user, note, exist.reaction), user)); Users.findOne(note.userId).then(u => { - deliver(user, content, u.inbox); + deliver(user, content, u!.inbox); }); } //#endregion diff --git a/src/services/push-notification.ts b/src/services/push-notification.ts index defd4d6e2d..1830cad623 100644 --- a/src/services/push-notification.ts +++ b/src/services/push-notification.ts @@ -1,26 +1,17 @@ import * as push from 'web-push'; import config from '../config'; import { SwSubscriptions } from '../models'; -import { Meta } from '../models/entities/meta'; import fetchMeta from '../misc/fetch-meta'; -let meta: Meta = null; - -setInterval(() => { - fetchMeta().then(m => { - meta = m; +export default async function(userId: string, type: string, body?: any) { + const meta = await fetchMeta(); - if (meta.enableServiceWorker) { - // アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録 - push.setVapidDetails(config.url, - meta.swPublicKey, - meta.swPrivateKey); - } - }); -}, 3000); + if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; -export default async function(userId: string, type: string, body?: any) { - if (!meta.enableServiceWorker) return; + // アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録 + push.setVapidDetails(config.url, + meta.swPublicKey, + meta.swPrivateKey); // Fetch const subscriptions = await SwSubscriptions.find({ diff --git a/src/services/register-or-fetch-instance-doc.ts b/src/services/register-or-fetch-instance-doc.ts index 459f538e96..9957edd3db 100644 --- a/src/services/register-or-fetch-instance-doc.ts +++ b/src/services/register-or-fetch-instance-doc.ts @@ -5,8 +5,6 @@ import { genId } from '../misc/gen-id'; import { toPuny } from '../misc/convert-host'; export async function registerOrFetchInstanceDoc(host: string): Promise { - if (host == null) return null; - host = toPuny(host); const index = await Instances.findOne({ host }); diff --git a/src/services/stream.ts b/src/services/stream.ts index c1d14b2779..c52af48b59 100644 --- a/src/services/stream.ts +++ b/src/services/stream.ts @@ -6,7 +6,7 @@ import { UserList } from '../models/entities/user-list'; import { ReversiGame } from '../models/entities/games/reversi/game'; class Publisher { - private ev: Xev; + private ev: Xev | null = null; constructor() { // Redisがインストールされてないときはプロセス間通信を使う @@ -15,7 +15,7 @@ class Publisher { } } - private publish = (channel: string, type: string, value?: any): void => { + private publish = (channel: string, type: string | null, value?: any): void => { const message = type == null ? value : value == null ? { type: type, body: null } : { type: type, body: value }; @@ -23,7 +23,7 @@ class Publisher { if (this.ev) { this.ev.emit(channel, message); } else { - redis.publish('misskey', JSON.stringify({ + redis!.publish('misskey', JSON.stringify({ channel: channel, message: message })); diff --git a/src/tools/clean-remote-files.ts b/src/tools/clean-remote-files.ts index f64affea97..e722552e14 100644 --- a/src/tools/clean-remote-files.ts +++ b/src/tools/clean-remote-files.ts @@ -3,6 +3,7 @@ import del from '../services/drive/delete-file'; import { DriveFiles } from '../models'; import { Not } from 'typeorm'; import { DriveFile } from '../models/entities/drive-file'; +import { ensure } from '../prelude/ensure'; const limit = promiseLimit(16); @@ -17,7 +18,7 @@ DriveFiles.find({ }); async function job(file: DriveFile): Promise { - file = await DriveFiles.findOne(file.id); + file = await DriveFiles.findOne(file.id).then(ensure); await del(file, true); diff --git a/src/tools/show-signin-history.ts b/src/tools/show-signin-history.ts index 584bece6bb..9e6e849f5d 100644 --- a/src/tools/show-signin-history.ts +++ b/src/tools/show-signin-history.ts @@ -9,7 +9,7 @@ import { Users, Signins } from '../models'; // node built/tools/show-signin-history username all // with full request headers -async function main(username: string, headers: string[]) { +async function main(username: string, headers?: string[]) { const user = await Users.findOne({ host: null, usernameLower: username.toLowerCase(), @@ -39,7 +39,7 @@ async function main(username: string, headers: string[]) { const args = process.argv.slice(2); let username = args[0]; -let headers: string[]; +let headers: string[] | undefined; if (args[1] != null) { headers = args[1].split(/,/).map(header => header.toLowerCase()); diff --git a/tsconfig.json b/tsconfig.json index 6bd8571207..4f1d1b9cd5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "removeComments": false, "noLib": false, "strict": true, - "strictNullChecks": false, + "strictNullChecks": true, + "strictPropertyInitialization": false, "experimentalDecorators": true, "emitDecoratorMetadata": true, "resolveJsonModule": true, -- cgit v1.2.3-freya From a51fbd73165caa1d79c0c030aa011e6e01d12fc6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 13 Apr 2019 04:00:02 +0900 Subject: Suppress errors --- src/server/api/endpoints/i/update-email.ts | 4 ++-- src/server/api/private/signup.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/server/api/endpoints/i') diff --git a/src/server/api/endpoints/i/update-email.ts b/src/server/api/endpoints/i/update-email.ts index 8b05f3c8cb..15c62a9d08 100644 --- a/src/server/api/endpoints/i/update-email.ts +++ b/src/server/api/endpoints/i/update-email.ts @@ -76,12 +76,12 @@ export default define(meta, async (ps, user) => { user: meta.smtpUser, pass: meta.smtpPass } : undefined - }); + } as any); const link = `${config.url}/verify-email/${code}`; transporter.sendMail({ - from: meta.email, + from: meta.email!, to: ps.email, subject: meta.name || 'Misskey', text: `To verify email, please click this link: ${link}` diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index ea4df060f8..4842a534bf 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -96,7 +96,7 @@ export default async (ctx: Koa.BaseContext) => { cipher: undefined, passphrase: undefined } - }, (e, publicKey, privateKey) => + } as any, (e, publicKey, privateKey) => e ? j(e) : s([publicKey, privateKey]) )); -- cgit v1.2.3-freya