diff options
Diffstat (limited to 'src/server/api/endpoints')
47 files changed, 770 insertions, 182 deletions
diff --git a/src/server/api/endpoints/admin/drive/files.ts b/src/server/api/endpoints/admin/drive/files.ts new file mode 100644 index 0000000000..dfd8eee91e --- /dev/null +++ b/src/server/api/endpoints/admin/drive/files.ts @@ -0,0 +1,81 @@ +import $ from 'cafy'; +import File, { packMany } from '../../../../../models/drive-file'; +import define from '../../../define'; + +export const meta = { + requireCredential: false, + requireModerator: true, + + params: { + limit: { + validator: $.num.optional.range(1, 100), + default: 10 + }, + + offset: { + validator: $.num.optional.min(0), + default: 0 + }, + + sort: { + validator: $.str.optional.or([ + '+createdAt', + '-createdAt', + '+size', + '-size', + ]), + }, + + origin: { + validator: $.str.optional.or([ + 'combined', + 'local', + 'remote', + ]), + default: 'local' + } + } +}; + +export default define(meta, (ps, me) => new Promise(async (res, rej) => { + let _sort; + if (ps.sort) { + if (ps.sort == '+createdAt') { + _sort = { + uploadDate: -1 + }; + } else if (ps.sort == '-createdAt') { + _sort = { + uploadDate: 1 + }; + } else if (ps.sort == '+size') { + _sort = { + length: -1 + }; + } else if (ps.sort == '-size') { + _sort = { + length: 1 + }; + } + } else { + _sort = { + _id: -1 + }; + } + + const q = { + 'metadata.deletedAt': { $exists: false }, + } as any; + + if (ps.origin == 'local') q['metadata._user.host'] = null; + if (ps.origin == 'remote') q['metadata._user.host'] = { $ne: null }; + + const files = await File + .find(q, { + limit: ps.limit, + sort: _sort, + skip: ps.offset + }); + + res(await packMany(files, { detail: true, withUser: true })); +})); diff --git a/src/server/api/endpoints/admin/reset-password.ts b/src/server/api/endpoints/admin/reset-password.ts new file mode 100644 index 0000000000..c072c12e0d --- /dev/null +++ b/src/server/api/endpoints/admin/reset-password.ts @@ -0,0 +1,57 @@ +import $ from 'cafy'; +import ID, { transform } from '../../../../misc/cafy-id'; +import define from '../../define'; +import User from '../../../../models/user'; +import * as bcrypt from 'bcryptjs'; +import rndstr from 'rndstr'; + +export const meta = { + desc: { + 'ja-JP': '指定したユーザーのパスワードをリセットします。', + }, + + requireCredential: true, + requireModerator: true, + + params: { + userId: { + validator: $.type(ID), + transform: transform, + desc: { + 'ja-JP': '対象のユーザーID', + 'en-US': 'The user ID which you want to suspend' + } + }, + } +}; + +export default define(meta, (ps) => new Promise(async (res, rej) => { + const user = await User.findOne({ + _id: ps.userId + }); + + if (user == null) { + return rej('user not found'); + } + + if (user.isAdmin) { + return rej('cannot reset password of admin'); + } + + const passwd = rndstr('a-zA-Z0-9', 8); + + // Generate hash of password + const hash = bcrypt.hashSync(passwd); + + await User.findOneAndUpdate({ + _id: user._id + }, { + $set: { + password: hash + } + }); + + res({ + password: passwd + }); +})); diff --git a/src/server/api/endpoints/admin/show-user.ts b/src/server/api/endpoints/admin/show-user.ts new file mode 100644 index 0000000000..490b685352 --- /dev/null +++ b/src/server/api/endpoints/admin/show-user.ts @@ -0,0 +1,40 @@ +import $ from 'cafy'; +import ID, { transform } from '../../../../misc/cafy-id'; +import define from '../../define'; +import User from '../../../../models/user'; + +export const meta = { + desc: { + 'ja-JP': '指定したユーザーの情報を取得します。', + }, + + requireCredential: true, + requireModerator: true, + + params: { + userId: { + validator: $.type(ID), + transform: transform, + desc: { + 'ja-JP': '対象のユーザーID', + 'en-US': 'The user ID which you want to suspend' + } + }, + } +}; + +export default define(meta, (ps, me) => new Promise(async (res, rej) => { + const user = await User.findOne({ + _id: ps.userId + }); + + if (user == null) { + return rej('user not found'); + } + + if (me.isModerator && user.isAdmin) { + return rej('cannot show info of admin'); + } + + res(user); +})); diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index bbae212bd7..6ceb2a98dc 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -46,6 +46,13 @@ export const meta = { } }, + errorImageUrl: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'インスタンスのエラー画像URL' + } + }, + name: { validator: $.str.optional.nullable, desc: { @@ -139,6 +146,13 @@ export const meta = { } }, + summalyProxy: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'summalyプロキシURL' + } + }, + enableTwitterIntegration: { validator: $.bool.optional, desc: { @@ -200,7 +214,98 @@ export const meta = { desc: { 'ja-JP': 'DiscordアプリのClient Secret' } - } + }, + + enableExternalUserRecommendation: { + validator: $.bool.optional, + desc: { + 'ja-JP': '外部ユーザーレコメンデーションを有効にする' + } + }, + + externalUserRecommendationEngine: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': '外部ユーザーレコメンデーションのサードパーティエンジン' + } + }, + + externalUserRecommendationTimeout: { + validator: $.num.optional.nullable.min(0), + desc: { + 'ja-JP': '外部ユーザーレコメンデーションのタイムアウト (ミリ秒)' + } + }, + + enableEmail: { + validator: $.bool.optional, + desc: { + 'ja-JP': 'メール配信を有効にするか否か' + } + }, + + email: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'メール配信する際に利用するメールアドレス' + } + }, + + smtpSecure: { + validator: $.bool.optional, + desc: { + 'ja-JP': 'SMTPサーバがSSLを使用しているか否か' + } + }, + + smtpHost: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'SMTPサーバのホスト' + } + }, + + smtpPort: { + validator: $.num.optional.nullable, + desc: { + 'ja-JP': 'SMTPサーバのポート' + } + }, + + smtpUser: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'SMTPサーバのユーザー名' + } + }, + + smtpPass: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'SMTPサーバのパスワード' + } + }, + + enableServiceWorker: { + validator: $.bool.optional, + desc: { + 'ja-JP': 'ServiceWorkerを有効にするか否か' + } + }, + + swPublicKey: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'ServiceWorkerのVAPIDキーペアの公開鍵' + } + }, + + swPrivateKey: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'ServiceWorkerのVAPIDキーペアの秘密鍵' + } + }, } }; @@ -279,6 +384,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { set.langs = ps.langs; } + if (ps.summalyProxy !== undefined) { + set.summalyProxy = ps.summalyProxy; + } + if (ps.enableTwitterIntegration !== undefined) { set.enableTwitterIntegration = ps.enableTwitterIntegration; } @@ -315,6 +424,62 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { set.discordClientSecret = ps.discordClientSecret; } + if (ps.enableExternalUserRecommendation !== undefined) { + set.enableExternalUserRecommendation = ps.enableExternalUserRecommendation; + } + + if (ps.externalUserRecommendationEngine !== undefined) { + set.externalUserRecommendationEngine = ps.externalUserRecommendationEngine; + } + + if (ps.externalUserRecommendationTimeout !== undefined) { + set.externalUserRecommendationTimeout = ps.externalUserRecommendationTimeout; + } + + if (ps.enableEmail !== undefined) { + set.enableEmail = ps.enableEmail; + } + + if (ps.email !== undefined) { + set.email = ps.email; + } + + if (ps.smtpSecure !== undefined) { + set.smtpSecure = ps.smtpSecure; + } + + if (ps.smtpHost !== undefined) { + set.smtpHost = ps.smtpHost; + } + + if (ps.smtpPort !== undefined) { + set.smtpPort = ps.smtpPort; + } + + if (ps.smtpUser !== undefined) { + set.smtpUser = ps.smtpUser; + } + + if (ps.smtpPass !== undefined) { + set.smtpPass = ps.smtpPass; + } + + if (ps.errorImageUrl !== undefined) { + set.errorImageUrl = ps.errorImageUrl; + } + + if (ps.enableServiceWorker !== undefined) { + set.enableServiceWorker = ps.enableServiceWorker; + } + + if (ps.swPublicKey !== undefined) { + set.swPublicKey = ps.swPublicKey; + } + + if (ps.swPrivateKey !== undefined) { + set.swPrivateKey = ps.swPrivateKey; + } + await Meta.update({}, { $set: set }, { upsert: true }); diff --git a/src/server/api/endpoints/aggregation/hashtags.ts b/src/server/api/endpoints/aggregation/hashtags.ts index f8fc7162f5..7577711b5f 100644 --- a/src/server/api/endpoints/aggregation/hashtags.ts +++ b/src/server/api/endpoints/aggregation/hashtags.ts @@ -47,10 +47,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { }> = []; // カウント - data.map(x => x._id).forEach(x => { - // ブラックリストに登録されているタグなら弾く - if (hidedTags.includes(x.tag)) return; - + for (const x of data.map(x => x._id).filter(x => !hidedTags.includes(x.tag))) { const i = tags.findIndex(tag => tag.name == x.tag); if (i != -1) { tags[i].count++; @@ -60,10 +57,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { count: 1 }); } - }); + } // タグを人気順に並べ替え - tags = tags.sort((a, b) => b.count - a.count); + tags.sort((a, b) => b.count - a.count); tags = tags.slice(0, 30); diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index 778d1e5099..3b4021e0a7 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -25,11 +25,10 @@ export const meta = { }, }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const object = await fetchAny(ps.uri); - if (object == null) return rej('object not found'); - - res(object); +export default define(meta, (ps) => new Promise((res, rej) => { + fetchAny(ps.uri) + .then(object => object != null ? res(object) : rej('object not found')) + .catch(e => rej(e)); })); /*** @@ -80,7 +79,7 @@ async function fetchAny(uri: string) { const user = await createPerson(object.id); return { type: 'User', - object: user + object: await packUser(user, null, { detail: true }) }; } @@ -88,7 +87,7 @@ async function fetchAny(uri: string) { const note = await createNote(object.id); return { type: 'Note', - object: note + object: await packNote(note, null, { detail: true }) }; } diff --git a/src/server/api/endpoints/charts/drive.ts b/src/server/api/endpoints/charts/drive.ts index 25ede16102..2fd0139e8e 100644 --- a/src/server/api/endpoints/charts/drive.ts +++ b/src/server/api/endpoints/charts/drive.ts @@ -3,6 +3,8 @@ import define from '../../define'; import driveChart from '../../../../chart/drive'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ドライブのチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/federation.ts b/src/server/api/endpoints/charts/federation.ts index fb5fa4426e..f7d12e5c33 100644 --- a/src/server/api/endpoints/charts/federation.ts +++ b/src/server/api/endpoints/charts/federation.ts @@ -3,6 +3,8 @@ import define from '../../define'; import federationChart from '../../../../chart/federation'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'フェデレーションのチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/hashtag.ts b/src/server/api/endpoints/charts/hashtag.ts index b2140068a5..3df2dd8500 100644 --- a/src/server/api/endpoints/charts/hashtag.ts +++ b/src/server/api/endpoints/charts/hashtag.ts @@ -3,6 +3,8 @@ import define from '../../define'; import hashtagChart from '../../../../chart/hashtag'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ハッシュタグごとのチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/network.ts b/src/server/api/endpoints/charts/network.ts index 6d7468f950..af5b799a80 100644 --- a/src/server/api/endpoints/charts/network.ts +++ b/src/server/api/endpoints/charts/network.ts @@ -3,6 +3,8 @@ import define from '../../define'; import networkChart from '../../../../chart/network'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ネットワークのチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/notes.ts b/src/server/api/endpoints/charts/notes.ts index c9889d1840..906cef1fa9 100644 --- a/src/server/api/endpoints/charts/notes.ts +++ b/src/server/api/endpoints/charts/notes.ts @@ -3,6 +3,8 @@ import define from '../../define'; import notesChart from '../../../../chart/notes'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': '投稿のチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/user/drive.ts b/src/server/api/endpoints/charts/user/drive.ts index 7e806c151b..1ef133c6bf 100644 --- a/src/server/api/endpoints/charts/user/drive.ts +++ b/src/server/api/endpoints/charts/user/drive.ts @@ -4,6 +4,8 @@ import perUserDriveChart from '../../../../../chart/per-user-drive'; import ID, { transform } from '../../../../../misc/cafy-id'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ユーザーごとのドライブのチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/user/following.ts b/src/server/api/endpoints/charts/user/following.ts index ee20b3c39c..c8a64455e5 100644 --- a/src/server/api/endpoints/charts/user/following.ts +++ b/src/server/api/endpoints/charts/user/following.ts @@ -4,6 +4,8 @@ import perUserFollowingChart from '../../../../../chart/per-user-following'; import ID, { transform } from '../../../../../misc/cafy-id'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ユーザーごとのフォロー/フォロワーのチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/user/notes.ts b/src/server/api/endpoints/charts/user/notes.ts index 1b6a312a01..f8a3c726ef 100644 --- a/src/server/api/endpoints/charts/user/notes.ts +++ b/src/server/api/endpoints/charts/user/notes.ts @@ -4,6 +4,8 @@ import perUserNotesChart from '../../../../../chart/per-user-notes'; import ID, { transform } from '../../../../../misc/cafy-id'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ユーザーごとの投稿のチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/user/reactions.ts b/src/server/api/endpoints/charts/user/reactions.ts index 78bd22cc2f..f24c84593c 100644 --- a/src/server/api/endpoints/charts/user/reactions.ts +++ b/src/server/api/endpoints/charts/user/reactions.ts @@ -4,6 +4,8 @@ import perUserReactionsChart from '../../../../../chart/per-user-reactions'; import ID, { transform } from '../../../../../misc/cafy-id'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ユーザーごとの被リアクション数のチャートを取得します。' }, diff --git a/src/server/api/endpoints/charts/users.ts b/src/server/api/endpoints/charts/users.ts index 733b7b8178..00c2871148 100644 --- a/src/server/api/endpoints/charts/users.ts +++ b/src/server/api/endpoints/charts/users.ts @@ -3,6 +3,8 @@ import define from '../../define'; import usersChart from '../../../../chart/users'; export const meta = { + stability: 'stable', + desc: { 'ja-JP': 'ユーザーのチャートを取得します。' }, diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts index 27f101562d..20955e0e4e 100644 --- a/src/server/api/endpoints/drive/files.ts +++ b/src/server/api/endpoints/drive/files.ts @@ -77,5 +77,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(files)); + res(await packMany(files, { detail: false, self: true })); })); diff --git a/src/server/api/endpoints/drive/files/check_existence.ts b/src/server/api/endpoints/drive/files/check_existence.ts index d3ba4b386d..6e986d4170 100644 --- a/src/server/api/endpoints/drive/files/check_existence.ts +++ b/src/server/api/endpoints/drive/files/check_existence.ts @@ -32,6 +32,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { if (file === null) { res({ file: null }); } else { - res({ file: await pack(file) }); + res({ file: await pack(file, { self: true }) }); } })); diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index 53c62dd868..0660627f08 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -74,7 +74,7 @@ export default define(meta, (ps, user, app, file, cleanup) => new Promise(async cleanup(); - res(pack(driveFile)); + res(pack(driveFile, { self: true })); } catch (e) { console.error(e); diff --git a/src/server/api/endpoints/drive/files/delete.ts b/src/server/api/endpoints/drive/files/delete.ts index 7367c8fbb6..0c2799c708 100644 --- a/src/server/api/endpoints/drive/files/delete.ts +++ b/src/server/api/endpoints/drive/files/delete.ts @@ -32,14 +32,17 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // Fetch file const file = await DriveFile .findOne({ - _id: ps.fileId, - 'metadata.userId': user._id + _id: ps.fileId }); if (file === null) { return rej('file-not-found'); } + if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) { + return rej('access denied'); + } + // Delete await del(file); diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts index 8bc392fefe..25135e83a2 100644 --- a/src/server/api/endpoints/drive/files/find.ts +++ b/src/server/api/endpoints/drive/files/find.ts @@ -31,5 +31,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { 'metadata.folderId': ps.folderId }); - res(await Promise.all(files.map(file => pack(file)))); + res(await Promise.all(files.map(file => pack(file, { self: true })))); })); diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts index 450a97065b..95c3323fbb 100644 --- a/src/server/api/endpoints/drive/files/show.ts +++ b/src/server/api/endpoints/drive/files/show.ts @@ -41,7 +41,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // Serialize const _file = await pack(file, { - detail: true + detail: true, + self: true }); res(_file); diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts index 4efec3dc2a..a17ff2bf34 100644 --- a/src/server/api/endpoints/drive/files/update.ts +++ b/src/server/api/endpoints/drive/files/update.ts @@ -57,14 +57,17 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // Fetch file const file = await DriveFile .findOne({ - _id: ps.fileId, - 'metadata.userId': user._id + _id: ps.fileId }); if (file === null) { return rej('file-not-found'); } + if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) { + return rej('access denied'); + } + if (ps.name) file.filename = ps.name; if (ps.isSensitive !== undefined) file.metadata.isSensitive = ps.isSensitive; @@ -100,18 +103,18 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { Note.find({ '_files._id': file._id }).then(notes => { - notes.forEach(note => { + for (const note of notes) { note._files[note._files.findIndex(f => f._id.equals(file._id))] = file; Note.update({ _id: note._id }, { $set: { _files: note._files } }); - }); + } }); // Serialize - const fileObj = await pack(file); + const fileObj = await pack(file, { self: true }); // Response res(fileObj); diff --git a/src/server/api/endpoints/drive/files/upload_from_url.ts b/src/server/api/endpoints/drive/files/upload_from_url.ts index a8faab1d73..fc386e1638 100644 --- a/src/server/api/endpoints/drive/files/upload_from_url.ts +++ b/src/server/api/endpoints/drive/files/upload_from_url.ts @@ -26,7 +26,7 @@ export const meta = { folderId: { validator: $.type(ID).optional.nullable, - default: null as any as any, + default: null as any, transform: transform }, @@ -50,5 +50,5 @@ export const meta = { }; export default define(meta, (ps, user) => new Promise(async (res, rej) => { - res(pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force))); + res(pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force), { self: true })); })); diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts index 804ecf50d9..c8342c66b5 100644 --- a/src/server/api/endpoints/drive/stream.ts +++ b/src/server/api/endpoints/drive/stream.ts @@ -65,5 +65,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(files)); + res(await packMany(files, { self: true })); })); diff --git a/src/server/api/endpoints/games/reversi/games/show.ts b/src/server/api/endpoints/games/reversi/games/show.ts index c747202354..0882456102 100644 --- a/src/server/api/endpoints/games/reversi/games/show.ts +++ b/src/server/api/endpoints/games/reversi/games/show.ts @@ -25,9 +25,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { loopedBoard: game.settings.loopedBoard }); - game.logs.forEach(log => { + for (const log of game.logs) o.put(log.color, log.pos); - }); const packed = await pack(game, user); diff --git a/src/server/api/endpoints/games/reversi/match.ts b/src/server/api/endpoints/games/reversi/match.ts index 43b6fc8eed..a3cc523a8c 100644 --- a/src/server/api/endpoints/games/reversi/match.ts +++ b/src/server/api/endpoints/games/reversi/match.ts @@ -66,7 +66,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (other == 0) { - publishMainStream(user._id, 'reversi_no_invites'); + publishMainStream(user._id, 'reversiNoInvites'); } } else { // Fetch child diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts index ed4c8e337f..a26dbf0941 100644 --- a/src/server/api/endpoints/hashtags/trend.ts +++ b/src/server/api/endpoints/hashtags/trend.ts @@ -58,10 +58,7 @@ export default define(meta, () => new Promise(async (res, rej) => { }> = []; // カウント - data.map(x => x._id).forEach(x => { - // ブラックリストに登録されているタグなら弾く - if (hidedTags.includes(x.tag)) return; - + for (const x of data.map(x => x._id).filter(x => !hidedTags.includes(x.tag))) { const i = tags.findIndex(tag => tag.name == x.tag); if (i != -1) { tags[i].count++; @@ -71,7 +68,7 @@ export default define(meta, () => new Promise(async (res, rej) => { count: 1 }); } - }); + } // 最低要求投稿者数を下回るならカットする const limitedTags = tags.filter(tag => tag.count >= requiredUsers); diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index aea47ad795..ab85b17d09 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -1,4 +1,4 @@ -import User, { pack } from '../../../models/user'; +import { pack } from '../../../models/user'; import define from '../define'; export const meta = { @@ -27,11 +27,4 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { includeHasUnreadNotes: true, includeSecrets: isSecure })); - - // Update lastUsedAt - User.update({ _id: user._id }, { - $set: { - lastUsedAt: new Date() - } - }); })); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 4952b2f010..7bdd52883c 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -6,6 +6,9 @@ import acceptAllFollowRequests from '../../../../services/following/requests/acc import { publishToFollowers } from '../../../../services/i/update'; import define from '../../define'; import getDriveFileUrl from '../../../../misc/get-drive-file-url'; +import parse from '../../../../mfm/parse'; +import extractEmojis from '../../../../misc/extract-emojis'; +const langmap = require('langmap'); export const meta = { desc: { @@ -32,6 +35,13 @@ export const meta = { } }, + lang: { + validator: $.str.optional.nullable.or(Object.keys(langmap)), + desc: { + 'ja-JP': '言語' + } + }, + location: { validator: $.str.optional.nullable.pipe(isValidLocation), desc: { @@ -121,6 +131,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { 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.avatarId !== undefined) updates.avatarId = ps.avatarId; @@ -182,6 +193,24 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { } } + //#region emojis + if (updates.name != null || updates.description != null) { + let emojis = [] as string[]; + + if (updates.name != null) { + const tokens = parse(updates.name, true); + emojis = emojis.concat(extractEmojis(tokens)); + } + + if (updates.description != null) { + const tokens = parse(updates.description); + emojis = emojis.concat(extractEmojis(tokens)); + } + + updates.emojis = emojis; + } + //#endregion + await User.update(user._id, { $set: updates }); diff --git a/src/server/api/endpoints/i/update_email.ts b/src/server/api/endpoints/i/update_email.ts new file mode 100644 index 0000000000..e08d1fba05 --- /dev/null +++ b/src/server/api/endpoints/i/update_email.ts @@ -0,0 +1,100 @@ +import $ from 'cafy'; +import User, { pack } from '../../../../models/user'; +import { publishMainStream } from '../../../../stream'; +import define from '../../define'; +import * as nodemailer from 'nodemailer'; +import fetchMeta from '../../../../misc/fetch-meta'; +import rndstr from 'rndstr'; +import config from '../../../../config'; +const ms = require('ms'); +import * as bcrypt from 'bcryptjs'; + +export const meta = { + requireCredential: true, + + secure: true, + + limit: { + duration: ms('1hour'), + max: 3 + }, + + params: { + password: { + validator: $.str + }, + + email: { + validator: $.str.optional.nullable + }, + } +}; + +export default define(meta, (ps, user) => new Promise(async (res, rej) => { + // Compare password + const same = await bcrypt.compare(ps.password, user.password); + + if (!same) { + return rej('incorrect password'); + } + + await User.update(user._id, { + $set: { + email: ps.email, + emailVerified: false, + emailVerifyCode: null + } + }); + + // Serialize + const iObj = await pack(user._id, user, { + detail: true, + includeSecrets: true + }); + + // Send response + res(iObj); + + // Publish meUpdated event + publishMainStream(user._id, 'meUpdated', iObj); + + if (ps.email != null) { + const code = rndstr('a-z0-9', 16); + + await User.update(user._id, { + $set: { + emailVerifyCode: code + } + }); + + const meta = await fetchMeta(); + + const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; + + const transporter = nodemailer.createTransport({ + host: meta.smtpHost, + port: meta.smtpPort, + secure: meta.smtpSecure, + ignoreTLS: !enableAuth, + auth: enableAuth ? { + user: meta.smtpUser, + pass: meta.smtpPass + } : undefined + }); + + const link = `${config.url}/verify-email/${code}`; + + transporter.sendMail({ + from: meta.email, + to: ps.email, + subject: meta.name, + text: `To verify email, please click this link: ${link}` + }, (error, info) => { + if (error) { + return console.error(error); + } + + console.log('Message sent: %s', info.messageId); + }); + } +})); diff --git a/src/server/api/endpoints/i/update_widget.ts b/src/server/api/endpoints/i/update_widget.ts index 90fe8fbe23..da96ec6fc1 100644 --- a/src/server/api/endpoints/i/update_widget.ts +++ b/src/server/api/endpoints/i/update_widget.ts @@ -59,11 +59,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { //#region Deck if (widget == null && user.clientSettings.deck && user.clientSettings.deck.columns) { const deck = user.clientSettings.deck; - deck.columns.filter((c: any) => c.type == 'widgets').forEach((c: any) => { - c.widgets.forEach((w: any) => { - if (w.id == ps.id) widget = w; - }); - }); + 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; diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 9846e95959..57311426a8 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -63,11 +63,12 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { cacheRemoteFiles: instance.cacheRemoteFiles, enableRecaptcha: instance.enableRecaptcha, recaptchaSiteKey: instance.recaptchaSiteKey, - swPublickey: config.sw ? config.sw.public_key : null, + swPublickey: instance.swPublicKey, bannerUrl: instance.bannerUrl, + errorImageUrl: instance.errorImageUrl, maxNoteTextLength: instance.maxNoteTextLength, - emojis: emojis, + enableEmail: instance.enableEmail, enableTwitterIntegration: instance.enableTwitterIntegration, enableGithubIntegration: instance.enableGithubIntegration, @@ -84,8 +85,12 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { twitter: instance.enableTwitterIntegration, github: instance.enableGithubIntegration, discord: instance.enableDiscordIntegration, - serviceWorker: config.sw ? true : false, - userRecommendation: config.user_recommendation ? config.user_recommendation : {} + serviceWorker: instance.enableServiceWorker, + userRecommendation: { + external: instance.enableExternalUserRecommendation, + engine: instance.externalUserRecommendationEngine, + timeout: instance.externalUserRecommendationTimeout + } }; } @@ -99,6 +104,17 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { response.githubClientSecret = instance.githubClientSecret; response.discordClientId = instance.discordClientId; response.discordClientSecret = instance.discordClientSecret; + response.enableExternalUserRecommendation = instance.enableExternalUserRecommendation; + response.externalUserRecommendationEngine = instance.externalUserRecommendationEngine; + response.externalUserRecommendationTimeout = instance.externalUserRecommendationTimeout; + response.summalyProxy = instance.summalyProxy; + response.email = instance.email; + response.smtpSecure = instance.smtpSecure; + response.smtpHost = instance.smtpHost; + response.smtpPort = instance.smtpPort; + response.smtpUser = instance.smtpUser; + response.smtpPass = instance.smtpPass; + response.swPrivateKey = instance.swPrivateKey; } res(response); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index 5edc56165e..b489197076 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -78,6 +78,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { _id: -1 }; const query = { + deletedAt: null, visibility: 'public' } as any; if (ps.sinceId) { @@ -105,9 +106,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { const withFiles = ps.withFiles != undefined ? ps.withFiles : ps.media; - if (withFiles) { - query.fileIds = withFiles ? { $exists: true, $ne: null } : []; - } + if (withFiles) query.fileIds = { $exists: true, $ne: null }; if (ps.poll != undefined) { query.poll = ps.poll ? { $exists: true, $ne: null } : null; diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 4f8d6a4f4f..e8c37664f5 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -42,7 +42,7 @@ export const meta = { }, visibleUserIds: { - validator: $.arr($.type(ID)).optional.unique().min(1), + validator: $.arr($.type(ID)).optional.unique().min(0), transform: transformMany, desc: { 'ja-JP': '(投稿の公開範囲が specified の場合)投稿を閲覧できるユーザー' @@ -82,6 +82,30 @@ export const meta = { } }, + noExtractMentions: { + validator: $.bool.optional, + default: false, + desc: { + 'ja-JP': '本文からメンションを展開しないか否か。' + } + }, + + noExtractHashtags: { + validator: $.bool.optional, + default: false, + desc: { + 'ja-JP': '本文からハッシュタグを展開しないか否か。' + } + }, + + noExtractEmojis: { + validator: $.bool.optional, + default: false, + desc: { + 'ja-JP': '本文からカスタム絵文字を展開しないか否か。' + } + }, + geo: { validator: $.obj({ coordinates: $.arr().length(2) @@ -219,7 +243,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { } // テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー - if ((ps.text == null) && files === null && renote === null && ps.poll == null) { + if (!(ps.text || files.length || renote || ps.poll)) { return rej('text, fileIds, renoteId or poll is required'); } @@ -237,6 +261,9 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { localOnly: ps.localOnly, visibility: ps.visibility, visibleUsers, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, geo: ps.geo }) .then(note => pack(note, user)) diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts index aa11f7bf19..1923aed9ba 100644 --- a/src/server/api/endpoints/notes/delete.ts +++ b/src/server/api/endpoints/notes/delete.ts @@ -3,6 +3,7 @@ import Note from '../../../../models/note'; import deleteNote from '../../../../services/note/delete'; import User from '../../../../models/user'; import define from '../../define'; +const ms = require('ms'); export const meta = { stability: 'stable', @@ -16,6 +17,12 @@ export const meta = { kind: 'note-write', + limit: { + duration: ms('1hour'), + max: 300, + minInterval: ms('1sec') + }, + params: { noteId: { validator: $.type(ID), diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 718f5e4403..a353165cd0 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -91,5 +91,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { res(await packMany(mentions, user)); - mentions.forEach(note => read(user._id, note._id)); + 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 cfcba788ec..2bc1a4f913 100644 --- a/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/src/server/api/endpoints/notes/polls/recommendation.ts @@ -39,6 +39,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { const notes = await Note .find({ + '_user.host': null, _id: { $nin: nin }, diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index dc012152c6..8de0eb420e 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -102,12 +102,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }) .then(watchers => { - watchers.forEach(watcher => { + for (const watcher of watchers) { notify(watcher.userId, user._id, 'poll_vote', { noteId: note._id, choice: ps.choice }); - }); + } }); // この投稿をWatchする diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 6046b9b310..6c2b690ab2 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -33,16 +33,13 @@ export const meta = { }; export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId - }); - if (note === null) { - return rej('note not found'); - } - - const ids = (note._replyIds || []).slice(ps.offset, ps.offset + ps.limit); + const notes = await Note.find({ + replyId: ps.noteId + }, { + limit: ps.limit, + skip: ps.offset + }); - res(await packMany(ids, user)); + res(await packMany(notes, user)); })); diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts index 26427452fd..095c1b765d 100644 --- a/src/server/api/endpoints/sw/register.ts +++ b/src/server/api/endpoints/sw/register.ts @@ -1,7 +1,7 @@ import $ from 'cafy'; import Subscription from '../../../../models/sw-subscription'; -import config from '../../../../config'; import define from '../../define'; +import fetchMeta from '../../../../misc/fetch-meta'; export const meta = { requireCredential: true, @@ -31,10 +31,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { deletedAt: { $exists: false } }); + const instance = await fetchMeta(); + if (exist != null) { return res({ state: 'already-subscribed', - key: config.sw.public_key + key: instance.swPublicKey }); } @@ -47,6 +49,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { res({ state: 'subscribed', - key: config.sw.public_key + key: instance.swPublicKey }); })); diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts index 203b4a53c8..aef5bd8507 100644 --- a/src/server/api/endpoints/users.ts +++ b/src/server/api/endpoints/users.ts @@ -17,7 +17,23 @@ export const meta = { }, sort: { - validator: $.str.optional.or('+follower|-follower'), + validator: $.str.optional.or([ + '+follower', + '-follower', + '+createdAt', + '-createdAt', + '+updatedAt', + '-updatedAt', + ]), + }, + + origin: { + validator: $.str.optional.or([ + 'combined', + 'local', + 'remote', + ]), + default: 'local' } } }; @@ -33,6 +49,22 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { _sort = { followersCount: 1 }; + } else if (ps.sort == '+createdAt') { + _sort = { + createdAt: -1 + }; + } else if (ps.sort == '+updatedAt') { + _sort = { + updatedAt: -1 + }; + } else if (ps.sort == '-createdAt') { + _sort = { + createdAt: 1 + }; + } else if (ps.sort == '-updatedAt') { + _sort = { + updatedAt: 1 + }; } } else { _sort = { @@ -40,14 +72,17 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }; } + const q = + ps.origin == 'local' ? { host: null } : + ps.origin == 'remote' ? { host: { $ne: null } } : + {}; + const users = await User - .find({ - host: null - }, { + .find(q, { limit: ps.limit, sort: _sort, skip: ps.offset }); - res(await Promise.all(users.map(user => pack(user, me)))); + res(await Promise.all(users.map(user => pack(user, me, { detail: 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 cf123b82ba..353ccef1e9 100644 --- a/src/server/api/endpoints/users/get_frequently_replied_users.ts +++ b/src/server/api/endpoints/users/get_frequently_replied_users.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import Note from '../../../../models/note'; import User, { pack } from '../../../../models/user'; import define from '../../define'; +import { maximum } from '../../../../prelude/array'; export const meta = { requireCredential: false, @@ -77,20 +78,16 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { const repliedUsers: any = {}; // Extract replies from recent notes - replyTargetNotes.forEach(note => { - const userId = note.userId.toString(); + for (const userId of replyTargetNotes.map(x => x.userId.toString())) { if (repliedUsers[userId]) { repliedUsers[userId]++; } else { repliedUsers[userId] = 1; } - }); + } // Calc peak - let peak = 0; - Object.keys(repliedUsers).forEach(user => { - if (repliedUsers[user] > peak) peak = repliedUsers[user]; - }); + const peak = maximum(Object.values(repliedUsers)); // Sort replies by frequency const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); diff --git a/src/server/api/endpoints/users/lists/pull.ts b/src/server/api/endpoints/users/lists/pull.ts new file mode 100644 index 0000000000..f1b25127b3 --- /dev/null +++ b/src/server/api/endpoints/users/lists/pull.ts @@ -0,0 +1,64 @@ +import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; +import UserList from '../../../../../models/user-list'; +import User, { pack as packUser } from '../../../../../models/user'; +import { publishUserListStream } from '../../../../../stream'; +import define from '../../../define'; + +export const meta = { + desc: { + 'ja-JP': '指定したユーザーリストから指定したユーザーを削除します。', + 'en-US': 'Remove a user to a user list.' + }, + + requireCredential: true, + + kind: 'account-write', + + params: { + listId: { + validator: $.type(ID), + transform: transform, + }, + + userId: { + validator: $.type(ID), + transform: transform, + desc: { + 'ja-JP': '対象のユーザーのID', + 'en-US': 'Target user ID' + } + }, + } +}; + +export default define(meta, (ps, me) => new Promise(async (res, rej) => { + // Fetch the list + const userList = await UserList.findOne({ + _id: ps.listId, + userId: me._id, + }); + + if (userList == null) { + return rej('list not found'); + } + + // Fetch the user + const user = await User.findOne({ + _id: ps.userId + }); + + if (user == null) { + return rej('user not found'); + } + + // Pull the user + await UserList.update({ _id: userList._id }, { + $pull: { + userIds: user._id + } + }); + + res(); + + publishUserListStream(userList._id, 'userRemoved', await packUser(user)); +})); diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index 127029f83c..bacace6a6a 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -1,11 +1,13 @@ const ms = require('ms'); import $ from 'cafy'; -import User, { pack } from '../../../../models/user'; +import User, { pack, ILocalUser } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; import Mute from '../../../../models/mute'; -import * as request from 'request'; +import * as request from 'request-promise-native'; import config from '../../../../config'; import define from '../../define'; +import fetchMeta from '../../../../misc/fetch-meta'; +import resolveUser from '../../../../remote/resolve-user'; export const meta = { desc: { @@ -30,13 +32,15 @@ export const meta = { }; export default define(meta, (ps, me) => new Promise(async (res, rej) => { - if (config.user_recommendation && config.user_recommendation.external) { + const instance = await fetchMeta(); + + if (instance.enableExternalUserRecommendation) { const userName = me.username; const hostName = config.hostname; const limit = ps.limit; const offset = ps.offset; - const timeout = config.user_recommendation.timeout; - const engine = config.user_recommendation.engine; + const timeout = instance.externalUserRecommendationTimeout; + const engine = instance.externalUserRecommendationEngine; const url = engine .replace('{{host}}', hostName) .replace('{{user}}', userName) @@ -50,13 +54,10 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { json: true, followRedirect: true, followAllRedirects: true - }, (error: any, response: any, body: any) => { - if (!error && response.statusCode == 200) { - res(body); - } else { - res([]); - } - }); + }) + .then(body => convertUsers(body, me)) + .then(packed => res(packed)) + .catch(e => rej(e)); } else { // ID list of the user itself and other users who the user follows const followingIds = await getFriendIds(me._id); @@ -72,7 +73,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { $nin: followingIds.concat(mutedUserIds) }, isLocked: { $ne: true }, - lastUsedAt: { + updatedAt: { $gte: new Date(Date.now() - ms('7days')) }, host: null @@ -87,3 +88,30 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); } })); + +type IRecommendUser = { + name: string; + username: string; + host: string; + description: string; + avatarUrl: string; +}; + +/** + * Resolve/Pack dummy users + */ +async function convertUsers(src: IRecommendUser[], me: ILocalUser) { + const packed = await Promise.all(src.map(async x => { + const user = await resolveUser(x.username, x.host) + .catch(() => { + console.warn(`Can't resolve ${x.username}@${x.host}`); + return null; + }); + + if (user == null) return x; + + return await pack(user, me, { detail: true }); + })); + + return packed; +} diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index edc4d603ca..86b16dcbb1 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -41,11 +41,19 @@ export const meta = { 'ja-JP': 'ローカルユーザーのみ検索対象にするか否か' } }, + + detail: { + validator: $.bool.optional, + default: true, + desc: { + 'ja-JP': '詳細なユーザー情報を含めるか否か' + } + }, }, }; export default define(meta, (ps, me) => new Promise(async (res, rej) => { - const isUsername = validateUsername(ps.query.replace('@', '')); + const isUsername = validateUsername(ps.query.replace('@', ''), !ps.localOnly); let users: IUser[] = []; @@ -70,86 +78,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { users = users.concat(otherUsers); } - - if (users.length < ps.limit) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: null, - usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase())) - }, { - limit: ps.limit - users.length - }); - - users = users.concat(otherUsers); - } - - if (users.length < ps.limit && !ps.localOnly) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: { $ne: null }, - usernameLower: new RegExp(escapeRegexp(ps.query.replace('@', '').toLowerCase())) - }, { - limit: ps.limit - users.length - }); - - users = users.concat(otherUsers); - } - } - - if (users.length < ps.limit) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: null, - name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase())) - }, { - limit: ps.limit - users.length - }); - - users = users.concat(otherUsers); - } - - if (users.length < ps.limit && !ps.localOnly) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: { $ne: null }, - name: new RegExp('^' + escapeRegexp(ps.query.toLowerCase())) - }, { - limit: ps.limit - users.length - }); - - users = users.concat(otherUsers); - } - - if (users.length < ps.limit) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: null, - name: new RegExp(escapeRegexp(ps.query.toLowerCase())) - }, { - limit: ps.limit - users.length - }); - - users = users.concat(otherUsers); - } - - if (users.length < ps.limit && !ps.localOnly) { - const otherUsers = await User - .find({ - _id: { $nin: users.map(u => u._id) }, - host: { $ne: null }, - name: new RegExp(escapeRegexp(ps.query.toLowerCase())) - }, { - limit: ps.limit - users.length - }); - - users = users.concat(otherUsers); } - // Serialize - res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); + res(await Promise.all(users.map(user => pack(user, me, { detail: ps.detail })))); })); diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index 6e4cf514de..fd26554709 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -80,7 +80,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { })); if (isRemoteUser(user)) { - if (user.updatedAt == null || Date.now() - user.updatedAt.getTime() > 1000 * 60 * 60 * 24) { + if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { resolveRemoteUser(ps.username, ps.host, { }, true); } } |