diff options
Diffstat (limited to 'packages/backend/src/server/api/endpoints/admin')
11 files changed, 223 insertions, 17 deletions
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts new file mode 100644 index 0000000000..2d7ef2f236 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -0,0 +1,31 @@ +import { Users } from '@/models/index.js'; +import { deleteAccount } from '@/services/delete-account.js'; +import define from '../../define.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireAdmin: true, + + res: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps) => { + const user = await Users.findOneByOrFail({ id: ps.userId }); + if (user.isDeleted) { + return; + } + + await deleteAccount(user); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts new file mode 100644 index 0000000000..a4b29770e1 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -0,0 +1,47 @@ +import define from '../../define.js'; +import { Users } from '@/models/index.js'; +import { User } from '@/models/entities/user.js'; +import { insertModerationLog } from '@/services/insert-moderation-log.js'; +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + overrideMb: { type: 'number', nullable: true }, + }, + required: ['userId', 'overrideMb'], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, me) => { + const user = await Users.findOneBy({ id: ps.userId }); + + if (user == null) { + throw new Error('user not found'); + } + + if (!Users.isLocalUser(user)) { + throw new Error('user is not local user'); + } + + /*if (user.isAdmin) { + throw new Error('cannot suspend admin'); + } + if (user.isModerator) { + throw new Error('cannot suspend moderator'); + }*/ + + await Users.update(user.id, { + driveCapacityOverrideMb: ps.overrideMb, + }); + + insertModerationLog(me, 'change-drive-capacity-override', { + targetId: user.id, + }); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 119c4db19b..ba32aac431 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,5 @@ -import define from '../../../define.js'; import { DriveFiles } from '@/models/index.js'; +import define from '../../../define.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; export const meta = { @@ -25,8 +25,9 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id', nullable: true }, type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) }, - origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, hostname: { type: 'string', nullable: true, @@ -41,14 +42,18 @@ export const paramDef = { export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); - if (ps.origin === 'local') { - query.andWhere('file.userHost IS NULL'); - } else if (ps.origin === 'remote') { - query.andWhere('file.userHost IS NOT NULL'); - } + if (ps.userId) { + query.andWhere('file.userId = :userId', { userId: ps.userId }); + } else { + if (ps.origin === 'local') { + query.andWhere('file.userHost IS NULL'); + } else if (ps.origin === 'remote') { + query.andWhere('file.userHost IS NOT NULL'); + } - if (ps.hostname) { - query.andWhere('file.userHost = :hostname', { hostname: ps.hostname }); + if (ps.hostname) { + query.andWhere('file.userHost = :hostname', { hostname: ps.hostname }); + } } if (ps.type) { diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 039df74f1b..e9117a23c8 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,6 +1,6 @@ +import { DriveFiles } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { DriveFiles } from '@/models/index.js'; export const meta = { tags: ['admin'], @@ -184,5 +184,10 @@ export default define(meta, paramDef, async (ps, me) => { throw new ApiError(meta.errors.noSuchFile); } + if (!me.isAdmin) { + delete file.requestIp; + delete file.requestHeaders; + } + return file; }); diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts new file mode 100644 index 0000000000..e8b9cb3b09 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -0,0 +1,31 @@ +import { UserIps } from '@/models/index.js'; +import define from '../../define.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireAdmin: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, me) => { + const ips = await UserIps.find({ + where: { userId: ps.userId }, + order: { createdAt: 'DESC' }, + take: 30, + }); + + return ips.map(x => ({ + ip: x.ip, + createdAt: x.createdAt.toISOString(), + })); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 8d50486ef6..cb50e128af 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,7 +1,7 @@ import config from '@/config/index.js'; -import define from '../../define.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import define from '../../define.js'; export const meta = { tags: ['meta'], @@ -195,6 +195,22 @@ export const meta = { type: 'string', optional: true, nullable: true, }, + sensitiveMediaDetection: { + type: 'string', + optional: true, nullable: false, + }, + sensitiveMediaDetectionSensitivity: { + type: 'string', + optional: true, nullable: false, + }, + setSensitiveFlagAutomatically: { + type: 'boolean', + optional: true, nullable: false, + }, + enableSensitiveMediaDetectionForVideos: { + type: 'boolean', + optional: true, nullable: false, + }, proxyAccountId: { type: 'string', optional: true, nullable: true, @@ -304,6 +320,10 @@ export const meta = { type: 'boolean', optional: true, nullable: false, }, + enableIpLogging: { + type: 'boolean', + optional: true, nullable: false, + }, }, }, } as const; @@ -360,13 +380,16 @@ export default define(meta, paramDef, async (ps, me) => { pinnedPages: instance.pinnedPages, pinnedClipId: instance.pinnedClipId, cacheRemoteFiles: instance.cacheRemoteFiles, - useStarForReactionFallback: instance.useStarForReactionFallback, pinnedUsers: instance.pinnedUsers, hiddenTags: instance.hiddenTags, blockedHosts: instance.blockedHosts, hcaptchaSecretKey: instance.hcaptchaSecretKey, recaptchaSecretKey: instance.recaptchaSecretKey, + sensitiveMediaDetection: instance.sensitiveMediaDetection, + sensitiveMediaDetectionSensitivity: instance.sensitiveMediaDetectionSensitivity, + setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically, + enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, proxyAccountId: instance.proxyAccountId, twitterConsumerKey: instance.twitterConsumerKey, twitterConsumerSecret: instance.twitterConsumerSecret, @@ -397,5 +420,6 @@ export default define(meta, paramDef, async (ps, me) => { objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle, deeplAuthKey: instance.deeplAuthKey, deeplIsPro: instance.deeplIsPro, + enableIpLogging: instance.enableIpLogging, }; }); diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 9c150420b1..85c6fb82e7 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -99,12 +99,16 @@ export default define(meta, paramDef, async () => { const fsStats = await si.fsSize(); const netInterface = await si.networkInterfaceDefault(); + const redisServerInfo = await redisClient.info('Server'); + const m = redisServerInfo.match(new RegExp('^redis_version:(.*)', 'm')); + const redis_version = m?.[1]; + return { machine: os.hostname(), os: os.platform(), node: process.version, psql: await db.query('SHOW server_version').then(x => x[0].server_version), - redis: redisClient.server_info.redis_version, + redis: redis_version, cpu: { model: os.cpus()[0].model, cores: os.cpus().length, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 78033aed58..0d866b3113 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -25,7 +25,7 @@ export const paramDef = { export default define(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ Users.findOneBy({ id: ps.userId }), - UserProfiles.findOneBy({ userId: ps.userId }) + UserProfiles.findOneBy({ userId: ps.userId }), ]); if (user == null || profile == null) { @@ -58,6 +58,7 @@ export default define(meta, paramDef, async (ps, me) => { autoAcceptFollowed: profile.autoAcceptFollowed, noCrawle: profile.noCrawle, alwaysMarkNsfw: profile.alwaysMarkNsfw, + autoSensitive: profile.autoSensitive, carefulBot: profile.carefulBot, injectFeaturedNote: profile.injectFeaturedNote, receiveAnnouncementEmail: profile.receiveAnnouncementEmail, @@ -68,6 +69,8 @@ export default define(meta, paramDef, async (ps, me) => { isModerator: user.isModerator, isSilenced: user.isSilenced, isSuspended: user.isSuspended, + lastActiveDate: user.lastActiveDate, + moderationNote: profile.moderationNote, signins, }; }); diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 1575d81d5d..8e09e72d5b 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -25,7 +25,7 @@ export const paramDef = { offset: { type: 'integer', default: 0 }, sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] }, state: { type: 'string', enum: ['all', 'alive', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: 'all' }, - origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, username: { type: 'string', nullable: true, default: null }, hostname: { type: 'string', @@ -61,7 +61,7 @@ export default define(meta, paramDef, async (ps, me) => { } if (ps.hostname) { - query.andWhere('user.host like :hostname', { hostname: '%' + ps.hostname.toLowerCase() + '%' }); + query.andWhere('user.host = :hostname', { hostname: ps.hostname.toLowerCase() }); } switch (ps.sort) { diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 09e43301b7..cc32e73c53 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,8 +1,8 @@ -import define from '../../define.js'; import { Meta } from '@/models/entities/meta.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { db } from '@/db/postgre.js'; +import define from '../../define.js'; export const meta = { tags: ['admin'], @@ -48,6 +48,10 @@ export const paramDef = { enableRecaptcha: { type: 'boolean' }, recaptchaSiteKey: { type: 'string', nullable: true }, recaptchaSecretKey: { type: 'string', nullable: true }, + sensitiveMediaDetection: { type: 'string', enum: ['none', 'all', 'local', 'remote'] }, + sensitiveMediaDetectionSensitivity: { type: 'string', enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'] }, + setSensitiveFlagAutomatically: { type: 'boolean' }, + enableSensitiveMediaDetectionForVideos: { type: 'boolean' }, proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true }, maintainerName: { type: 'string', nullable: true }, maintainerEmail: { type: 'string', nullable: true }, @@ -96,6 +100,7 @@ export const paramDef = { objectStorageUseProxy: { type: 'boolean' }, objectStorageSetPublicRead: { type: 'boolean' }, objectStorageS3ForcePathStyle: { type: 'boolean' }, + enableIpLogging: { type: 'boolean' }, }, required: [], } as const; @@ -212,6 +217,22 @@ export default define(meta, paramDef, async (ps, me) => { set.recaptchaSecretKey = ps.recaptchaSecretKey; } + if (ps.sensitiveMediaDetection !== undefined) { + set.sensitiveMediaDetection = ps.sensitiveMediaDetection; + } + + if (ps.sensitiveMediaDetectionSensitivity !== undefined) { + set.sensitiveMediaDetectionSensitivity = ps.sensitiveMediaDetectionSensitivity; + } + + if (ps.setSensitiveFlagAutomatically !== undefined) { + set.setSensitiveFlagAutomatically = ps.setSensitiveFlagAutomatically; + } + + if (ps.enableSensitiveMediaDetectionForVideos !== undefined) { + set.enableSensitiveMediaDetectionForVideos = ps.enableSensitiveMediaDetectionForVideos; + } + if (ps.proxyAccountId !== undefined) { set.proxyAccountId = ps.proxyAccountId; } @@ -396,6 +417,10 @@ export default define(meta, paramDef, async (ps, me) => { set.deeplIsPro = ps.deeplIsPro; } + if (ps.enableIpLogging !== undefined) { + set.enableIpLogging = ps.enableIpLogging; + } + await db.transaction(async transactionalEntityManager => { const metas = await transactionalEntityManager.find(Meta, { order: { diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts new file mode 100644 index 0000000000..fa21ab7833 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -0,0 +1,31 @@ +import { UserProfiles, Users } from '@/models/index.js'; +import define from '../../define.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireModerator: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + text: { type: 'string' }, + }, + required: ['userId', 'text'], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, me) => { + const user = await Users.findOneBy({ id: ps.userId }); + + if (user == null) { + throw new Error('user not found'); + } + + await UserProfiles.update({ userId: user.id }, { + moderationNote: ps.text, + }); +}); |