diff options
Diffstat (limited to 'src/server/api/endpoints/admin')
| -rw-r--r-- | src/server/api/endpoints/admin/abuse-user-reports.ts | 54 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/drive/files.ts | 81 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/drive/show-file.ts | 28 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/remove-abuse-user-report.ts | 32 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/reset-password.ts | 57 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/show-user.ts | 40 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/show-users.ts | 123 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/suspend-user.ts | 4 | ||||
| -rw-r--r-- | src/server/api/endpoints/admin/update-meta.ts | 189 |
9 files changed, 607 insertions, 1 deletions
diff --git a/src/server/api/endpoints/admin/abuse-user-reports.ts b/src/server/api/endpoints/admin/abuse-user-reports.ts new file mode 100644 index 0000000000..c88174f13f --- /dev/null +++ b/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -0,0 +1,54 @@ +import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; +import Report, { packMany } from '../../../../models/abuse-user-report'; +import define from '../../define'; + +export const meta = { + requireCredential: true, + requireModerator: true, + + params: { + limit: { + validator: $.num.optional.range(1, 100), + default: 10 + }, + + sinceId: { + validator: $.type(ID).optional, + transform: transform, + }, + + untilId: { + validator: $.type(ID).optional, + transform: transform, + }, + } +}; + +export default define(meta, (ps) => new Promise(async (res, rej) => { + if (ps.sinceId && ps.untilId) { + return rej('cannot set sinceId and untilId'); + } + + const sort = { + _id: -1 + }; + const query = {} as any; + if (ps.sinceId) { + sort._id = 1; + query._id = { + $gt: ps.sinceId + }; + } else if (ps.untilId) { + query._id = { + $lt: ps.untilId + }; + } + + const reports = await Report + .find(query, { + limit: ps.limit, + sort: sort + }); + + res(await packMany(reports)); +})); 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..177a808cbf --- /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, self: true })); +})); diff --git a/src/server/api/endpoints/admin/drive/show-file.ts b/src/server/api/endpoints/admin/drive/show-file.ts new file mode 100644 index 0000000000..6dfab19643 --- /dev/null +++ b/src/server/api/endpoints/admin/drive/show-file.ts @@ -0,0 +1,28 @@ +import $ from 'cafy'; +import ID, { transform } from '../../../../../misc/cafy-id'; +import define from '../../../define'; +import DriveFile from '../../../../../models/drive-file'; + +export const meta = { + requireCredential: true, + requireModerator: true, + + params: { + fileId: { + validator: $.type(ID), + transform: transform, + }, + } +}; + +export default define(meta, (ps, me) => new Promise(async (res, rej) => { + const file = await DriveFile.findOne({ + _id: ps.fileId + }); + + if (file == null) { + return rej('file not found'); + } + + res(file); +})); diff --git a/src/server/api/endpoints/admin/remove-abuse-user-report.ts b/src/server/api/endpoints/admin/remove-abuse-user-report.ts new file mode 100644 index 0000000000..4d068a410e --- /dev/null +++ b/src/server/api/endpoints/admin/remove-abuse-user-report.ts @@ -0,0 +1,32 @@ +import $ from 'cafy'; +import ID, { transform } from '../../../../misc/cafy-id'; +import define from '../../define'; +import AbuseUserReport from '../../../../models/abuse-user-report'; + +export const meta = { + requireCredential: true, + requireModerator: true, + + params: { + reportId: { + validator: $.type(ID), + transform: transform + }, + } +}; + +export default define(meta, (ps) => new Promise(async (res, rej) => { + const report = await AbuseUserReport.findOne({ + _id: ps.reportId + }); + + if (report == null) { + return rej('report not found'); + } + + await AbuseUserReport.remove({ + _id: report._id + }); + + res(); +})); 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/show-users.ts b/src/server/api/endpoints/admin/show-users.ts new file mode 100644 index 0000000000..20ccfbd7f3 --- /dev/null +++ b/src/server/api/endpoints/admin/show-users.ts @@ -0,0 +1,123 @@ +import $ from 'cafy'; +import User, { pack } from '../../../../models/user'; +import define from '../../define'; + +export const meta = { + requireCredential: true, + 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([ + '+follower', + '-follower', + '+createdAt', + '-createdAt', + '+updatedAt', + '-updatedAt', + ]), + }, + + state: { + validator: $.str.optional.or([ + 'all', + 'admin', + 'moderator', + 'adminOrModerator', + 'verified', + 'suspended', + ]), + default: 'all' + }, + + 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 == '+follower') { + _sort = { + followersCount: -1 + }; + } else if (ps.sort == '-follower') { + _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 = { + _id: -1 + }; + } + + const q = { + $and: [] + } as any; + + // state + q.$and.push( + ps.state == 'admin' ? { isAdmin: true } : + ps.state == 'moderator' ? { isModerator: true } : + ps.state == 'adminOrModerator' ? { + $or: [{ + isAdmin: true + }, { + isModerator: true + }] + } : + ps.state == 'verified' ? { isVerified: true } : + ps.state == 'suspended' ? { isSuspended: true } : + {} + ); + + // origin + q.$and.push( + ps.origin == 'local' ? { host: null } : + ps.origin == 'remote' ? { host: { $ne: null } } : + {} + ); + + const users = await User + .find(q, { + limit: ps.limit, + sort: _sort, + skip: ps.offset + }); + + res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); +})); diff --git a/src/server/api/endpoints/admin/suspend-user.ts b/src/server/api/endpoints/admin/suspend-user.ts index 5bbd387a20..2ec5196880 100644 --- a/src/server/api/endpoints/admin/suspend-user.ts +++ b/src/server/api/endpoints/admin/suspend-user.ts @@ -37,6 +37,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { return rej('cannot suspend admin'); } + if (user.isModerator) { + return rej('cannot suspend moderator'); + } + await User.findOneAndUpdate({ _id: user._id }, { diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index bbae212bd7..13663243a2 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -32,6 +32,13 @@ export const meta = { } }, + disableGlobalTimeline: { + validator: $.bool.optional.nullable, + desc: { + 'ja-JP': 'グローバルタイムラインを無効にするか否か' + } + }, + hidedTags: { validator: $.arr($.str).optional.nullable, desc: { @@ -39,6 +46,13 @@ export const meta = { } }, + mascotImageUrl: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'インスタンスキャラクター画像のURL' + } + }, + bannerUrl: { validator: $.str.optional.nullable, desc: { @@ -46,6 +60,13 @@ export const meta = { } }, + errorImageUrl: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'インスタンスのエラー画像URL' + } + }, + name: { validator: $.str.optional.nullable, desc: { @@ -139,6 +160,13 @@ export const meta = { } }, + summalyProxy: { + validator: $.str.optional.nullable, + desc: { + 'ja-JP': 'summalyプロキシURL' + } + }, + enableTwitterIntegration: { validator: $.bool.optional, desc: { @@ -200,7 +228,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キーペアの秘密鍵' + } + }, } }; @@ -219,10 +338,18 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { set.disableLocalTimeline = ps.disableLocalTimeline; } + if (typeof ps.disableGlobalTimeline === 'boolean') { + set.disableGlobalTimeline = ps.disableGlobalTimeline; + } + if (Array.isArray(ps.hidedTags)) { set.hidedTags = ps.hidedTags; } + if (ps.mascotImageUrl !== undefined) { + set.mascotImageUrl = ps.mascotImageUrl; + } + if (ps.bannerUrl !== undefined) { set.bannerUrl = ps.bannerUrl; } @@ -279,6 +406,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 +446,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 }); |