summaryrefslogtreecommitdiff
path: root/src/server/api/endpoints/admin
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/api/endpoints/admin')
-rw-r--r--src/server/api/endpoints/admin/abuse-user-reports.ts54
-rw-r--r--src/server/api/endpoints/admin/drive/files.ts81
-rw-r--r--src/server/api/endpoints/admin/drive/show-file.ts28
-rw-r--r--src/server/api/endpoints/admin/remove-abuse-user-report.ts32
-rw-r--r--src/server/api/endpoints/admin/reset-password.ts57
-rw-r--r--src/server/api/endpoints/admin/show-user.ts40
-rw-r--r--src/server/api/endpoints/admin/show-users.ts123
-rw-r--r--src/server/api/endpoints/admin/suspend-user.ts4
-rw-r--r--src/server/api/endpoints/admin/update-meta.ts189
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 });