mirror of
https://github.com/misskey-dev/misskey.git
synced 2025-01-01 00:40:43 +01:00
Merge branch 'develop' into feature/channel_muting
# Conflicts: # CHANGELOG.md # packages/backend/src/core/CoreModule.ts
This commit is contained in:
commit
ebba83f0e6
43 changed files with 854 additions and 98 deletions
|
@ -9,11 +9,15 @@
|
||||||
|
|
||||||
### General
|
### General
|
||||||
- Feat: サーバー初期設定時に初期パスワードを設定できるように
|
- Feat: サーバー初期設定時に初期パスワードを設定できるように
|
||||||
|
- Feat: 通報にモデレーションノートを残せるように
|
||||||
|
- Feat: 通報の解決種別を設定できるように
|
||||||
- Feat: チャンネルミュート機能の実装 #10649
|
- Feat: チャンネルミュート機能の実装 #10649
|
||||||
- チャンネルの概要画面の右上からミュートできます(リンクコピー、共有、設定と同列)
|
- チャンネルの概要画面の右上からミュートできます(リンクコピー、共有、設定と同列)
|
||||||
|
- Enhance: 通報の解決と転送を個別に行えるように
|
||||||
- Enhance: セキュリティ向上のため、サインイン時もCAPTCHAを求めるようになりました
|
- Enhance: セキュリティ向上のため、サインイン時もCAPTCHAを求めるようになりました
|
||||||
- Enhance: 依存関係の更新
|
- Enhance: 依存関係の更新
|
||||||
- Enhance: l10nの更新
|
- Enhance: l10nの更新
|
||||||
|
- Enhance: Playの「人気」タブで10件以上表示可能に #14399
|
||||||
- Fix: 連合のホワイトリストが正常に登録されない問題を修正
|
- Fix: 連合のホワイトリストが正常に登録されない問題を修正
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
|
55
locales/index.d.ts
vendored
55
locales/index.d.ts
vendored
|
@ -1834,6 +1834,10 @@ export interface Locale extends ILocale {
|
||||||
* モデレーションノート
|
* モデレーションノート
|
||||||
*/
|
*/
|
||||||
"moderationNote": string;
|
"moderationNote": string;
|
||||||
|
/**
|
||||||
|
* モデレーター間でだけ共有されるメモを記入することができます。
|
||||||
|
*/
|
||||||
|
"moderationNoteDescription": string;
|
||||||
/**
|
/**
|
||||||
* モデレーションノートを追加する
|
* モデレーションノートを追加する
|
||||||
*/
|
*/
|
||||||
|
@ -2894,22 +2898,10 @@ export interface Locale extends ILocale {
|
||||||
* 通報元
|
* 通報元
|
||||||
*/
|
*/
|
||||||
"reporterOrigin": string;
|
"reporterOrigin": string;
|
||||||
/**
|
|
||||||
* リモートサーバーに通報を転送する
|
|
||||||
*/
|
|
||||||
"forwardReport": string;
|
|
||||||
/**
|
|
||||||
* リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。
|
|
||||||
*/
|
|
||||||
"forwardReportIsAnonymous": string;
|
|
||||||
/**
|
/**
|
||||||
* 送信
|
* 送信
|
||||||
*/
|
*/
|
||||||
"send": string;
|
"send": string;
|
||||||
/**
|
|
||||||
* 対応済みにする
|
|
||||||
*/
|
|
||||||
"abuseMarkAsResolved": string;
|
|
||||||
/**
|
/**
|
||||||
* 新しいタブで開く
|
* 新しいタブで開く
|
||||||
*/
|
*/
|
||||||
|
@ -5170,6 +5162,37 @@ export interface Locale extends ILocale {
|
||||||
* フォロワーへのメッセージ
|
* フォロワーへのメッセージ
|
||||||
*/
|
*/
|
||||||
"messageToFollower": string;
|
"messageToFollower": string;
|
||||||
|
/**
|
||||||
|
* 対象
|
||||||
|
*/
|
||||||
|
"target": string;
|
||||||
|
"_abuseUserReport": {
|
||||||
|
/**
|
||||||
|
* 転送
|
||||||
|
*/
|
||||||
|
"forward": string;
|
||||||
|
/**
|
||||||
|
* 匿名のシステムアカウントとして、リモートサーバーに通報を転送します。
|
||||||
|
*/
|
||||||
|
"forwardDescription": string;
|
||||||
|
/**
|
||||||
|
* 解決
|
||||||
|
*/
|
||||||
|
"resolve": string;
|
||||||
|
/**
|
||||||
|
* 是認
|
||||||
|
*/
|
||||||
|
"accept": string;
|
||||||
|
/**
|
||||||
|
* 否認
|
||||||
|
*/
|
||||||
|
"reject": string;
|
||||||
|
/**
|
||||||
|
* 内容が正当である通報に対応した場合は「是認」を選択し、肯定的にケースが解決されたことをマークします。
|
||||||
|
* 内容が正当でない通報の場合は「否認」を選択し、否定的にケースが解決されたことをマークします。
|
||||||
|
*/
|
||||||
|
"resolveTutorial": string;
|
||||||
|
};
|
||||||
"_delivery": {
|
"_delivery": {
|
||||||
/**
|
/**
|
||||||
* 配信状態
|
* 配信状態
|
||||||
|
@ -9785,6 +9808,14 @@ export interface Locale extends ILocale {
|
||||||
* 通報を解決
|
* 通報を解決
|
||||||
*/
|
*/
|
||||||
"resolveAbuseReport": string;
|
"resolveAbuseReport": string;
|
||||||
|
/**
|
||||||
|
* 通報を転送
|
||||||
|
*/
|
||||||
|
"forwardAbuseReport": string;
|
||||||
|
/**
|
||||||
|
* 通報のモデレーションノート更新
|
||||||
|
*/
|
||||||
|
"updateAbuseReportNote": string;
|
||||||
/**
|
/**
|
||||||
* 招待コードを作成
|
* 招待コードを作成
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -454,6 +454,7 @@ totpDescription: "認証アプリを使ってワンタイムパスワードを
|
||||||
moderator: "モデレーター"
|
moderator: "モデレーター"
|
||||||
moderation: "モデレーション"
|
moderation: "モデレーション"
|
||||||
moderationNote: "モデレーションノート"
|
moderationNote: "モデレーションノート"
|
||||||
|
moderationNoteDescription: "モデレーター間でだけ共有されるメモを記入することができます。"
|
||||||
addModerationNote: "モデレーションノートを追加する"
|
addModerationNote: "モデレーションノートを追加する"
|
||||||
moderationLogs: "モデログ"
|
moderationLogs: "モデログ"
|
||||||
nUsersMentioned: "{n}人が投稿"
|
nUsersMentioned: "{n}人が投稿"
|
||||||
|
@ -719,10 +720,7 @@ abuseReported: "内容が送信されました。ご報告ありがとうござ
|
||||||
reporter: "通報者"
|
reporter: "通報者"
|
||||||
reporteeOrigin: "通報先"
|
reporteeOrigin: "通報先"
|
||||||
reporterOrigin: "通報元"
|
reporterOrigin: "通報元"
|
||||||
forwardReport: "リモートサーバーに通報を転送する"
|
|
||||||
forwardReportIsAnonymous: "リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。"
|
|
||||||
send: "送信"
|
send: "送信"
|
||||||
abuseMarkAsResolved: "対応済みにする"
|
|
||||||
openInNewTab: "新しいタブで開く"
|
openInNewTab: "新しいタブで開く"
|
||||||
openInSideView: "サイドビューで開く"
|
openInSideView: "サイドビューで開く"
|
||||||
defaultNavigationBehaviour: "デフォルトのナビゲーション"
|
defaultNavigationBehaviour: "デフォルトのナビゲーション"
|
||||||
|
@ -1288,6 +1286,15 @@ unknownWebAuthnKey: "登録されていないパスキーです。"
|
||||||
passkeyVerificationFailed: "パスキーの検証に失敗しました。"
|
passkeyVerificationFailed: "パスキーの検証に失敗しました。"
|
||||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "パスキーの検証に成功しましたが、パスワードレスログインが無効になっています。"
|
passkeyVerificationSucceededButPasswordlessLoginDisabled: "パスキーの検証に成功しましたが、パスワードレスログインが無効になっています。"
|
||||||
messageToFollower: "フォロワーへのメッセージ"
|
messageToFollower: "フォロワーへのメッセージ"
|
||||||
|
target: "対象"
|
||||||
|
|
||||||
|
_abuseUserReport:
|
||||||
|
forward: "転送"
|
||||||
|
forwardDescription: "匿名のシステムアカウントとして、リモートサーバーに通報を転送します。"
|
||||||
|
resolve: "解決"
|
||||||
|
accept: "是認"
|
||||||
|
reject: "否認"
|
||||||
|
resolveTutorial: "内容が正当である通報に対応した場合は「是認」を選択し、肯定的にケースが解決されたことをマークします。\n内容が正当でない通報の場合は「否認」を選択し、否定的にケースが解決されたことをマークします。"
|
||||||
|
|
||||||
_delivery:
|
_delivery:
|
||||||
status: "配信状態"
|
status: "配信状態"
|
||||||
|
@ -2593,6 +2600,8 @@ _moderationLogTypes:
|
||||||
markSensitiveDriveFile: "ファイルをセンシティブ付与"
|
markSensitiveDriveFile: "ファイルをセンシティブ付与"
|
||||||
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
|
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
|
||||||
resolveAbuseReport: "通報を解決"
|
resolveAbuseReport: "通報を解決"
|
||||||
|
forwardAbuseReport: "通報を転送"
|
||||||
|
updateAbuseReportNote: "通報のモデレーションノート更新"
|
||||||
createInvitation: "招待コードを作成"
|
createInvitation: "招待コードを作成"
|
||||||
createAd: "広告を作成"
|
createAd: "広告を作成"
|
||||||
deleteAd: "広告を削除"
|
deleteAd: "広告を削除"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2024.10.0-beta.4",
|
"version": "2024.10.0-beta.5",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class RefineAbuseUserReport1728085812127 {
|
||||||
|
name = 'RefineAbuseUserReport1728085812127'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "moderationNote" character varying(8192) NOT NULL DEFAULT ''`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "resolvedAs" character varying(128)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "resolvedAs"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "moderationNote"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,8 +20,10 @@ export class AbuseReportService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.abuseUserReportsRepository)
|
@Inject(DI.abuseUserReportsRepository)
|
||||||
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
private abuseReportNotificationService: AbuseReportNotificationService,
|
private abuseReportNotificationService: AbuseReportNotificationService,
|
||||||
private queueService: QueueService,
|
private queueService: QueueService,
|
||||||
|
@ -77,16 +79,16 @@ export class AbuseReportService {
|
||||||
* - SystemWebhook
|
* - SystemWebhook
|
||||||
*
|
*
|
||||||
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
* @param params 通報内容. もし複数件の通報に対応した時のために、あらかじめ複数件を処理できる前提で考える
|
||||||
* @param operator 通報を処理したユーザ
|
* @param moderator 通報を処理したユーザ
|
||||||
* @see AbuseReportNotificationService.notify
|
* @see AbuseReportNotificationService.notify
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolve(
|
public async resolve(
|
||||||
params: {
|
params: {
|
||||||
reportId: string;
|
reportId: string;
|
||||||
forward: boolean;
|
resolvedAs: MiAbuseUserReport['resolvedAs'];
|
||||||
}[],
|
}[],
|
||||||
operator: MiUser,
|
moderator: MiUser,
|
||||||
) {
|
) {
|
||||||
const paramsMap = new Map(params.map(it => [it.reportId, it]));
|
const paramsMap = new Map(params.map(it => [it.reportId, it]));
|
||||||
const reports = await this.abuseUserReportsRepository.findBy({
|
const reports = await this.abuseUserReportsRepository.findBy({
|
||||||
|
@ -99,25 +101,15 @@ export class AbuseReportService {
|
||||||
|
|
||||||
await this.abuseUserReportsRepository.update(report.id, {
|
await this.abuseUserReportsRepository.update(report.id, {
|
||||||
resolved: true,
|
resolved: true,
|
||||||
assigneeId: operator.id,
|
assigneeId: moderator.id,
|
||||||
forwarded: ps.forward && report.targetUserHost !== null,
|
resolvedAs: ps.resolvedAs,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ps.forward && report.targetUserHost != null) {
|
|
||||||
const actor = await this.instanceActorService.getInstanceActor();
|
|
||||||
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
|
|
||||||
const contextAssignedFlag = this.apRendererService.addContext(flag);
|
|
||||||
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.moderationLogService
|
this.moderationLogService
|
||||||
.log(operator, 'resolveAbuseReport', {
|
.log(moderator, 'resolveAbuseReport', {
|
||||||
reportId: report.id,
|
reportId: report.id,
|
||||||
report: report,
|
report: report,
|
||||||
forwarded: ps.forward && report.targetUserHost !== null,
|
resolvedAs: ps.resolvedAs,
|
||||||
})
|
})
|
||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
@ -125,4 +117,62 @@ export class AbuseReportService {
|
||||||
return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
|
return this.abuseUserReportsRepository.findBy({ id: In(reports.map(it => it.id)) })
|
||||||
.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
|
.then(reports => this.abuseReportNotificationService.notifySystemWebhook(reports, 'abuseReportResolved'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async forward(
|
||||||
|
reportId: MiAbuseUserReport['id'],
|
||||||
|
moderator: MiUser,
|
||||||
|
) {
|
||||||
|
const report = await this.abuseUserReportsRepository.findOneByOrFail({ id: reportId });
|
||||||
|
|
||||||
|
if (report.targetUserHost == null) {
|
||||||
|
throw new Error('The target user host is null.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (report.forwarded) {
|
||||||
|
throw new Error('The report has already been forwarded.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.abuseUserReportsRepository.update(report.id, {
|
||||||
|
forwarded: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const actor = await this.instanceActorService.getInstanceActor();
|
||||||
|
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
|
||||||
|
|
||||||
|
const flag = this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment);
|
||||||
|
const contextAssignedFlag = this.apRendererService.addContext(flag);
|
||||||
|
this.queueService.deliver(actor, contextAssignedFlag, targetUser.inbox, false);
|
||||||
|
|
||||||
|
this.moderationLogService
|
||||||
|
.log(moderator, 'forwardAbuseReport', {
|
||||||
|
reportId: report.id,
|
||||||
|
report: report,
|
||||||
|
})
|
||||||
|
.then();
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async update(
|
||||||
|
reportId: MiAbuseUserReport['id'],
|
||||||
|
params: {
|
||||||
|
moderationNote?: MiAbuseUserReport['moderationNote'];
|
||||||
|
},
|
||||||
|
moderator: MiUser,
|
||||||
|
) {
|
||||||
|
const report = await this.abuseUserReportsRepository.findOneByOrFail({ id: reportId });
|
||||||
|
|
||||||
|
await this.abuseUserReportsRepository.update(report.id, {
|
||||||
|
moderationNote: params.moderationNote,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (params.moderationNote != null && report.moderationNote !== params.moderationNote) {
|
||||||
|
this.moderationLogService.log(moderator, 'updateAbuseReportNote', {
|
||||||
|
reportId: report.id,
|
||||||
|
report: report,
|
||||||
|
before: report.moderationNote,
|
||||||
|
after: params.moderationNote,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationSe
|
||||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||||
import { UserSearchService } from '@/core/UserSearchService.js';
|
import { UserSearchService } from '@/core/UserSearchService.js';
|
||||||
import { WebhookTestService } from '@/core/WebhookTestService.js';
|
import { WebhookTestService } from '@/core/WebhookTestService.js';
|
||||||
|
import { FlashService } from '@/core/FlashService.js';
|
||||||
import { ChannelMutingService } from '@/core/ChannelMutingService.js';
|
import { ChannelMutingService } from '@/core/ChannelMutingService.js';
|
||||||
import { AccountMoveService } from './AccountMoveService.js';
|
import { AccountMoveService } from './AccountMoveService.js';
|
||||||
import { AccountUpdateService } from './AccountUpdateService.js';
|
import { AccountUpdateService } from './AccountUpdateService.js';
|
||||||
|
@ -218,6 +219,7 @@ const $SystemWebhookService: Provider = { provide: 'SystemWebhookService', useEx
|
||||||
const $WebhookTestService: Provider = { provide: 'WebhookTestService', useExisting: WebhookTestService };
|
const $WebhookTestService: Provider = { provide: 'WebhookTestService', useExisting: WebhookTestService };
|
||||||
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
|
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
|
||||||
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
|
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
|
||||||
|
const $FlashService: Provider = { provide: 'FlashService', useExisting: FlashService };
|
||||||
const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
|
const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
|
||||||
const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
|
const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
|
||||||
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
|
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
|
||||||
|
@ -369,6 +371,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
WebhookTestService,
|
WebhookTestService,
|
||||||
UtilityService,
|
UtilityService,
|
||||||
FileInfoService,
|
FileInfoService,
|
||||||
|
FlashService,
|
||||||
SearchService,
|
SearchService,
|
||||||
ClipService,
|
ClipService,
|
||||||
FeaturedService,
|
FeaturedService,
|
||||||
|
@ -516,6 +519,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
$WebhookTestService,
|
$WebhookTestService,
|
||||||
$UtilityService,
|
$UtilityService,
|
||||||
$FileInfoService,
|
$FileInfoService,
|
||||||
|
$FlashService,
|
||||||
$SearchService,
|
$SearchService,
|
||||||
$ClipService,
|
$ClipService,
|
||||||
$FeaturedService,
|
$FeaturedService,
|
||||||
|
@ -664,6 +668,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||||
WebhookTestService,
|
WebhookTestService,
|
||||||
UtilityService,
|
UtilityService,
|
||||||
FileInfoService,
|
FileInfoService,
|
||||||
|
FlashService,
|
||||||
SearchService,
|
SearchService,
|
||||||
ClipService,
|
ClipService,
|
||||||
FeaturedService,
|
FeaturedService,
|
||||||
|
|
40
packages/backend/src/core/FlashService.ts
Normal file
40
packages/backend/src/core/FlashService.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { type FlashsRepository } from '@/models/_.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MisskeyPlay関係のService
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class FlashService {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.flashsRepository)
|
||||||
|
private flashRepository: FlashsRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人気のあるPlay一覧を取得する.
|
||||||
|
*/
|
||||||
|
public async featured(opts?: { offset?: number, limit: number }) {
|
||||||
|
const builder = this.flashRepository.createQueryBuilder('flash')
|
||||||
|
.andWhere('flash.likedCount > 0')
|
||||||
|
.andWhere('flash.visibility = :visibility', { visibility: 'public' })
|
||||||
|
.addOrderBy('flash.likedCount', 'DESC')
|
||||||
|
.addOrderBy('flash.updatedAt', 'DESC')
|
||||||
|
.addOrderBy('flash.id', 'DESC');
|
||||||
|
|
||||||
|
if (opts?.offset) {
|
||||||
|
builder.skip(opts.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.take(opts?.limit ?? 10);
|
||||||
|
|
||||||
|
return await builder.getMany();
|
||||||
|
}
|
||||||
|
}
|
|
@ -218,7 +218,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||||
private utilityService: UtilityService,
|
private utilityService: UtilityService,
|
||||||
private userBlockingService: UserBlockingService,
|
private userBlockingService: UserBlockingService,
|
||||||
) {
|
) {
|
||||||
this.updateNotesCountQueue = new CollapsedQueue(60 * 1000 * 5, this.collapseNotesCount, this.performUpdateNotesCount);
|
this.updateNotesCountQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -35,6 +35,8 @@ function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseUserRe
|
||||||
comment: 'This is a dummy report for testing purposes.',
|
comment: 'This is a dummy report for testing purposes.',
|
||||||
targetUserHost: null,
|
targetUserHost: null,
|
||||||
reporterHost: null,
|
reporterHost: null,
|
||||||
|
resolvedAs: null,
|
||||||
|
moderationNote: 'foo',
|
||||||
...override,
|
...override,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@ export class AbuseUserReportEntityService {
|
||||||
schema: 'UserDetailedNotMe',
|
schema: 'UserDetailedNotMe',
|
||||||
}) : null,
|
}) : null,
|
||||||
forwarded: report.forwarded,
|
forwarded: report.forwarded,
|
||||||
|
resolvedAs: report.resolvedAs,
|
||||||
|
moderationNote: report.moderationNote,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,8 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { FlashsRepository, FlashLikesRepository } from '@/models/_.js';
|
import type { FlashLikesRepository, FlashsRepository } from '@/models/_.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import type { } from '@/models/Blocking.js';
|
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
import type { MiFlash } from '@/models/Flash.js';
|
import type { MiFlash } from '@/models/Flash.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
@ -20,10 +18,8 @@ export class FlashEntityService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.flashsRepository)
|
@Inject(DI.flashsRepository)
|
||||||
private flashsRepository: FlashsRepository,
|
private flashsRepository: FlashsRepository,
|
||||||
|
|
||||||
@Inject(DI.flashLikesRepository)
|
@Inject(DI.flashLikesRepository)
|
||||||
private flashLikesRepository: FlashLikesRepository,
|
private flashLikesRepository: FlashLikesRepository,
|
||||||
|
|
||||||
private userEntityService: UserEntityService,
|
private userEntityService: UserEntityService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
) {
|
) {
|
||||||
|
@ -34,25 +30,36 @@ export class FlashEntityService {
|
||||||
src: MiFlash['id'] | MiFlash,
|
src: MiFlash['id'] | MiFlash,
|
||||||
me?: { id: MiUser['id'] } | null | undefined,
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
hint?: {
|
hint?: {
|
||||||
packedUser?: Packed<'UserLite'>
|
packedUser?: Packed<'UserLite'>,
|
||||||
|
likedFlashIds?: MiFlash['id'][],
|
||||||
},
|
},
|
||||||
): Promise<Packed<'Flash'>> {
|
): Promise<Packed<'Flash'>> {
|
||||||
const meId = me ? me.id : null;
|
const meId = me ? me.id : null;
|
||||||
const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
|
const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
return await awaitAll({
|
// { schema: 'UserDetailed' } すると無限ループするので注意
|
||||||
|
const user = hint?.packedUser ?? await this.userEntityService.pack(flash.user ?? flash.userId, me);
|
||||||
|
|
||||||
|
let isLiked = false;
|
||||||
|
if (meId) {
|
||||||
|
isLiked = hint?.likedFlashIds
|
||||||
|
? hint.likedFlashIds.includes(flash.id)
|
||||||
|
: await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
id: flash.id,
|
id: flash.id,
|
||||||
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
||||||
updatedAt: flash.updatedAt.toISOString(),
|
updatedAt: flash.updatedAt.toISOString(),
|
||||||
userId: flash.userId,
|
userId: flash.userId,
|
||||||
user: hint?.packedUser ?? this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
user: user,
|
||||||
title: flash.title,
|
title: flash.title,
|
||||||
summary: flash.summary,
|
summary: flash.summary,
|
||||||
script: flash.script,
|
script: flash.script,
|
||||||
visibility: flash.visibility,
|
visibility: flash.visibility,
|
||||||
likedCount: flash.likedCount,
|
likedCount: flash.likedCount,
|
||||||
isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined,
|
isLiked: isLiked,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -63,7 +70,19 @@ export class FlashEntityService {
|
||||||
const _users = flashes.map(({ user, userId }) => user ?? userId);
|
const _users = flashes.map(({ user, userId }) => user ?? userId);
|
||||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||||
.then(users => new Map(users.map(u => [u.id, u])));
|
.then(users => new Map(users.map(u => [u.id, u])));
|
||||||
return Promise.all(flashes.map(flash => this.pack(flash, me, { packedUser: _userMap.get(flash.userId) })));
|
const _likedFlashIds = me
|
||||||
|
? await this.flashLikesRepository.createQueryBuilder('flashLike')
|
||||||
|
.select('flashLike.flashId')
|
||||||
|
.where('flashLike.userId = :userId', { userId: me.id })
|
||||||
|
.getRawMany<{ flashLike_flashId: string }>()
|
||||||
|
.then(likes => [...new Set(likes.map(like => like.flashLike_flashId))])
|
||||||
|
: [];
|
||||||
|
return Promise.all(
|
||||||
|
flashes.map(flash => this.pack(flash, me, {
|
||||||
|
packedUser: _userMap.get(flash.userId),
|
||||||
|
likedFlashIds: _likedFlashIds,
|
||||||
|
})),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,9 @@ export class MiAbuseUserReport {
|
||||||
})
|
})
|
||||||
public resolved: boolean;
|
public resolved: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* リモートサーバーに転送したかどうか
|
||||||
|
*/
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
@ -60,6 +63,21 @@ export class MiAbuseUserReport {
|
||||||
})
|
})
|
||||||
public comment: string;
|
public comment: string;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 8192, default: '',
|
||||||
|
})
|
||||||
|
public moderationNote: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* accept 是認 ... 通報内容が正当であり、肯定的に対応された
|
||||||
|
* reject 否認 ... 通報内容が正当でなく、否定的に対応された
|
||||||
|
* null ... その他
|
||||||
|
*/
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 128, nullable: true,
|
||||||
|
})
|
||||||
|
public resolvedAs: 'accept' | 'reject' | null;
|
||||||
|
|
||||||
//#region Denormalized fields
|
//#region Denormalized fields
|
||||||
@Index()
|
@Index()
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
|
|
|
@ -7,6 +7,9 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typ
|
||||||
import { id } from './util/id.js';
|
import { id } from './util/id.js';
|
||||||
import { MiUser } from './User.js';
|
import { MiUser } from './User.js';
|
||||||
|
|
||||||
|
export const flashVisibility = ['public', 'private'] as const;
|
||||||
|
export type FlashVisibility = typeof flashVisibility[number];
|
||||||
|
|
||||||
@Entity('flash')
|
@Entity('flash')
|
||||||
export class MiFlash {
|
export class MiFlash {
|
||||||
@PrimaryColumn(id())
|
@PrimaryColumn(id())
|
||||||
|
@ -63,5 +66,5 @@ export class MiFlash {
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 512, default: 'public',
|
length: 512, default: 'public',
|
||||||
})
|
})
|
||||||
public visibility: 'public' | 'private';
|
public visibility: FlashVisibility;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class InboxProcessorService implements OnApplicationShutdown {
|
||||||
private queueLoggerService: QueueLoggerService,
|
private queueLoggerService: QueueLoggerService,
|
||||||
) {
|
) {
|
||||||
this.logger = this.queueLoggerService.logger.createSubLogger('inbox');
|
this.logger = this.queueLoggerService.logger.createSubLogger('inbox');
|
||||||
this.updateInstanceQueue = new CollapsedQueue(60 * 1000 * 5, this.collapseUpdateInstanceJobs, this.performUpdateInstance);
|
this.updateInstanceQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseUpdateInstanceJobs, this.performUpdateInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
|
|
@ -125,7 +125,7 @@ export class ApiServerService {
|
||||||
fastify.post<{
|
fastify.post<{
|
||||||
Body: {
|
Body: {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password?: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
credential?: AuthenticationResponseJSON;
|
credential?: AuthenticationResponseJSON;
|
||||||
'hcaptcha-response'?: string;
|
'hcaptcha-response'?: string;
|
||||||
|
|
|
@ -68,6 +68,8 @@ import * as ep___admin_relays_list from './endpoints/admin/relays/list.js';
|
||||||
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
|
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
|
||||||
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
|
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
|
||||||
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
|
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
|
||||||
|
import * as ep___admin_forwardAbuseUserReport from './endpoints/admin/forward-abuse-user-report.js';
|
||||||
|
import * as ep___admin_updateAbuseUserReport from './endpoints/admin/update-abuse-user-report.js';
|
||||||
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
||||||
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
||||||
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
||||||
|
@ -456,6 +458,8 @@ const $admin_relays_list: Provider = { provide: 'ep:admin/relays/list', useClass
|
||||||
const $admin_relays_remove: Provider = { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default };
|
const $admin_relays_remove: Provider = { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default };
|
||||||
const $admin_resetPassword: Provider = { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default };
|
const $admin_resetPassword: Provider = { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default };
|
||||||
const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default };
|
const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default };
|
||||||
|
const $admin_forwardAbuseUserReport: Provider = { provide: 'ep:admin/forward-abuse-user-report', useClass: ep___admin_forwardAbuseUserReport.default };
|
||||||
|
const $admin_updateAbuseUserReport: Provider = { provide: 'ep:admin/update-abuse-user-report', useClass: ep___admin_updateAbuseUserReport.default };
|
||||||
const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default };
|
const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default };
|
||||||
const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default };
|
const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default };
|
||||||
const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default };
|
const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default };
|
||||||
|
@ -848,6 +852,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||||
$admin_relays_remove,
|
$admin_relays_remove,
|
||||||
$admin_resetPassword,
|
$admin_resetPassword,
|
||||||
$admin_resolveAbuseUserReport,
|
$admin_resolveAbuseUserReport,
|
||||||
|
$admin_forwardAbuseUserReport,
|
||||||
|
$admin_updateAbuseUserReport,
|
||||||
$admin_sendEmail,
|
$admin_sendEmail,
|
||||||
$admin_serverInfo,
|
$admin_serverInfo,
|
||||||
$admin_showModerationLogs,
|
$admin_showModerationLogs,
|
||||||
|
@ -1234,6 +1240,8 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
|
||||||
$admin_relays_remove,
|
$admin_relays_remove,
|
||||||
$admin_resetPassword,
|
$admin_resetPassword,
|
||||||
$admin_resolveAbuseUserReport,
|
$admin_resolveAbuseUserReport,
|
||||||
|
$admin_forwardAbuseUserReport,
|
||||||
|
$admin_updateAbuseUserReport,
|
||||||
$admin_sendEmail,
|
$admin_sendEmail,
|
||||||
$admin_serverInfo,
|
$admin_serverInfo,
|
||||||
$admin_showModerationLogs,
|
$admin_showModerationLogs,
|
||||||
|
|
|
@ -74,6 +74,8 @@ import * as ep___admin_relays_list from './endpoints/admin/relays/list.js';
|
||||||
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
|
import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js';
|
||||||
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
|
import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js';
|
||||||
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
|
import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js';
|
||||||
|
import * as ep___admin_forwardAbuseUserReport from './endpoints/admin/forward-abuse-user-report.js';
|
||||||
|
import * as ep___admin_updateAbuseUserReport from './endpoints/admin/update-abuse-user-report.js';
|
||||||
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
import * as ep___admin_sendEmail from './endpoints/admin/send-email.js';
|
||||||
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
import * as ep___admin_serverInfo from './endpoints/admin/server-info.js';
|
||||||
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js';
|
||||||
|
@ -460,6 +462,8 @@ const eps = [
|
||||||
['admin/relays/remove', ep___admin_relays_remove],
|
['admin/relays/remove', ep___admin_relays_remove],
|
||||||
['admin/reset-password', ep___admin_resetPassword],
|
['admin/reset-password', ep___admin_resetPassword],
|
||||||
['admin/resolve-abuse-user-report', ep___admin_resolveAbuseUserReport],
|
['admin/resolve-abuse-user-report', ep___admin_resolveAbuseUserReport],
|
||||||
|
['admin/forward-abuse-user-report', ep___admin_forwardAbuseUserReport],
|
||||||
|
['admin/update-abuse-user-report', ep___admin_updateAbuseUserReport],
|
||||||
['admin/send-email', ep___admin_sendEmail],
|
['admin/send-email', ep___admin_sendEmail],
|
||||||
['admin/server-info', ep___admin_serverInfo],
|
['admin/server-info', ep___admin_serverInfo],
|
||||||
['admin/show-moderation-logs', ep___admin_showModerationLogs],
|
['admin/show-moderation-logs', ep___admin_showModerationLogs],
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import type { AbuseUserReportsRepository } from '@/models/_.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
import { AbuseReportService } from '@/core/AbuseReportService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireModerator: true,
|
||||||
|
kind: 'write:admin:resolve-abuse-user-report',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchAbuseReport: {
|
||||||
|
message: 'No such abuse report.',
|
||||||
|
code: 'NO_SUCH_ABUSE_REPORT',
|
||||||
|
id: '8763e21b-d9bc-40be-acf6-54c1a6986493',
|
||||||
|
kind: 'server',
|
||||||
|
httpStatusCode: 404,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
reportId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['reportId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.abuseUserReportsRepository)
|
||||||
|
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||||
|
private abuseReportService: AbuseReportService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
|
||||||
|
if (!report) {
|
||||||
|
throw new ApiError(meta.errors.noSuchAbuseReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.abuseReportService.forward(report.id, me);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
reportId: { type: 'string', format: 'misskey:id' },
|
reportId: { type: 'string', format: 'misskey:id' },
|
||||||
forward: { type: 'boolean', default: false },
|
resolvedAs: { type: 'string', enum: ['accept', 'reject', null], nullable: true },
|
||||||
},
|
},
|
||||||
required: ['reportId'],
|
required: ['reportId'],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -50,7 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw new ApiError(meta.errors.noSuchAbuseReport);
|
throw new ApiError(meta.errors.noSuchAbuseReport);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.abuseReportService.resolve([{ reportId: report.id, forward: ps.forward }], me);
|
await this.abuseReportService.resolve([{ reportId: report.id, resolvedAs: ps.resolvedAs ?? null }], me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import type { AbuseUserReportsRepository } from '@/models/_.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
import { AbuseReportService } from '@/core/AbuseReportService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requireModerator: true,
|
||||||
|
kind: 'write:admin:resolve-abuse-user-report',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchAbuseReport: {
|
||||||
|
message: 'No such abuse report.',
|
||||||
|
code: 'NO_SUCH_ABUSE_REPORT',
|
||||||
|
id: '15f51cf5-46d1-4b1d-a618-b35bcbed0662',
|
||||||
|
kind: 'server',
|
||||||
|
httpStatusCode: 404,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
reportId: { type: 'string', format: 'misskey:id' },
|
||||||
|
moderationNote: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['reportId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.abuseUserReportsRepository)
|
||||||
|
private abuseUserReportsRepository: AbuseUserReportsRepository,
|
||||||
|
private abuseReportService: AbuseReportService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
|
||||||
|
if (!report) {
|
||||||
|
throw new ApiError(meta.errors.noSuchAbuseReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.abuseReportService.update(report.id, {
|
||||||
|
moderationNote: ps.moderationNote,
|
||||||
|
}, me);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import type { FlashsRepository } from '@/models/_.js';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { FlashService } from '@/core/FlashService.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['flash'],
|
tags: ['flash'],
|
||||||
|
@ -27,26 +28,25 @@ export const meta = {
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {},
|
properties: {
|
||||||
|
offset: { type: 'integer', minimum: 0, default: 0 },
|
||||||
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DI.flashsRepository)
|
private flashService: FlashService,
|
||||||
private flashsRepository: FlashsRepository,
|
|
||||||
|
|
||||||
private flashEntityService: FlashEntityService,
|
private flashEntityService: FlashEntityService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
const query = this.flashsRepository.createQueryBuilder('flash')
|
const result = await this.flashService.featured({
|
||||||
.andWhere('flash.likedCount > 0')
|
offset: ps.offset,
|
||||||
.orderBy('flash.likedCount', 'DESC');
|
limit: ps.limit,
|
||||||
|
});
|
||||||
const flashs = await query.limit(10).getMany();
|
return await this.flashEntityService.packMany(result, me);
|
||||||
|
|
||||||
return await this.flashEntityService.packMany(flashs, me);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,8 @@ export const moderationLogTypes = [
|
||||||
'markSensitiveDriveFile',
|
'markSensitiveDriveFile',
|
||||||
'unmarkSensitiveDriveFile',
|
'unmarkSensitiveDriveFile',
|
||||||
'resolveAbuseReport',
|
'resolveAbuseReport',
|
||||||
|
'forwardAbuseReport',
|
||||||
|
'updateAbuseReportNote',
|
||||||
'createInvitation',
|
'createInvitation',
|
||||||
'createAd',
|
'createAd',
|
||||||
'updateAd',
|
'updateAd',
|
||||||
|
@ -267,7 +269,18 @@ export type ModerationLogPayloads = {
|
||||||
resolveAbuseReport: {
|
resolveAbuseReport: {
|
||||||
reportId: string;
|
reportId: string;
|
||||||
report: any;
|
report: any;
|
||||||
forwarded: boolean;
|
forwarded?: boolean;
|
||||||
|
resolvedAs?: string | null;
|
||||||
|
};
|
||||||
|
forwardAbuseReport: {
|
||||||
|
reportId: string;
|
||||||
|
report: any;
|
||||||
|
};
|
||||||
|
updateAbuseReportNote: {
|
||||||
|
reportId: string;
|
||||||
|
report: any;
|
||||||
|
before: string;
|
||||||
|
after: string;
|
||||||
};
|
};
|
||||||
createInvitation: {
|
createInvitation: {
|
||||||
invitations: any[];
|
invitations: any[];
|
||||||
|
|
|
@ -157,7 +157,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||||
const webhookBody2 = await captureWebhook(async () => {
|
const webhookBody2 = await captureWebhook(async () => {
|
||||||
await resolveAbuseReport({
|
await resolveAbuseReport({
|
||||||
reportId: webhookBody1.body.id,
|
reportId: webhookBody1.body.id,
|
||||||
forward: false,
|
|
||||||
}, admin);
|
}, admin);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -214,7 +213,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||||
const webhookBody2 = await captureWebhook(async () => {
|
const webhookBody2 = await captureWebhook(async () => {
|
||||||
await resolveAbuseReport({
|
await resolveAbuseReport({
|
||||||
reportId: abuseReportId,
|
reportId: abuseReportId,
|
||||||
forward: false,
|
|
||||||
}, admin);
|
}, admin);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -257,7 +255,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||||
const webhookBody2 = await captureWebhook(async () => {
|
const webhookBody2 = await captureWebhook(async () => {
|
||||||
await resolveAbuseReport({
|
await resolveAbuseReport({
|
||||||
reportId: webhookBody1.body.id,
|
reportId: webhookBody1.body.id,
|
||||||
forward: false,
|
|
||||||
}, admin);
|
}, admin);
|
||||||
}).catch(e => e.message);
|
}).catch(e => e.message);
|
||||||
|
|
||||||
|
@ -288,7 +285,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||||
const webhookBody2 = await captureWebhook(async () => {
|
const webhookBody2 = await captureWebhook(async () => {
|
||||||
await resolveAbuseReport({
|
await resolveAbuseReport({
|
||||||
reportId: abuseReportId,
|
reportId: abuseReportId,
|
||||||
forward: false,
|
|
||||||
}, admin);
|
}, admin);
|
||||||
}).catch(e => e.message);
|
}).catch(e => e.message);
|
||||||
|
|
||||||
|
@ -319,7 +315,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||||
const webhookBody2 = await captureWebhook(async () => {
|
const webhookBody2 = await captureWebhook(async () => {
|
||||||
await resolveAbuseReport({
|
await resolveAbuseReport({
|
||||||
reportId: abuseReportId,
|
reportId: abuseReportId,
|
||||||
forward: false,
|
|
||||||
}, admin);
|
}, admin);
|
||||||
}).catch(e => e.message);
|
}).catch(e => e.message);
|
||||||
|
|
||||||
|
@ -350,7 +345,6 @@ describe('[シナリオ] ユーザ通報', () => {
|
||||||
const webhookBody2 = await captureWebhook(async () => {
|
const webhookBody2 = await captureWebhook(async () => {
|
||||||
await resolveAbuseReport({
|
await resolveAbuseReport({
|
||||||
reportId: abuseReportId,
|
reportId: abuseReportId,
|
||||||
forward: false,
|
|
||||||
}, admin);
|
}, admin);
|
||||||
}).catch(e => e.message);
|
}).catch(e => e.message);
|
||||||
|
|
||||||
|
|
152
packages/backend/test/unit/FlashService.ts
Normal file
152
packages/backend/test/unit/FlashService.ts
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { FlashService } from '@/core/FlashService.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { FlashsRepository, MiFlash, MiUser, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { GlobalModule } from '@/GlobalModule.js';
|
||||||
|
|
||||||
|
describe('FlashService', () => {
|
||||||
|
let app: TestingModule;
|
||||||
|
let service: FlashService;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let flashsRepository: FlashsRepository;
|
||||||
|
let usersRepository: UsersRepository;
|
||||||
|
let userProfilesRepository: UserProfilesRepository;
|
||||||
|
let idService: IdService;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let root: MiUser;
|
||||||
|
let alice: MiUser;
|
||||||
|
let bob: MiUser;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function createFlash(data: Partial<MiFlash>) {
|
||||||
|
return flashsRepository.insert({
|
||||||
|
id: idService.gen(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
userId: root.id,
|
||||||
|
title: 'title',
|
||||||
|
summary: 'summary',
|
||||||
|
script: 'script',
|
||||||
|
permissions: [],
|
||||||
|
likedCount: 0,
|
||||||
|
...data,
|
||||||
|
}).then(x => flashsRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createUser(data: Partial<MiUser> = {}) {
|
||||||
|
const user = await usersRepository
|
||||||
|
.insert({
|
||||||
|
id: idService.gen(),
|
||||||
|
...data,
|
||||||
|
})
|
||||||
|
.then(x => usersRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
|
await userProfilesRepository.insert({
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
app = await Test.createTestingModule({
|
||||||
|
imports: [
|
||||||
|
GlobalModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
FlashService,
|
||||||
|
IdService,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = app.get(FlashService);
|
||||||
|
|
||||||
|
flashsRepository = app.get(DI.flashsRepository);
|
||||||
|
usersRepository = app.get(DI.usersRepository);
|
||||||
|
userProfilesRepository = app.get(DI.userProfilesRepository);
|
||||||
|
idService = app.get(IdService);
|
||||||
|
|
||||||
|
root = await createUser({ username: 'root', usernameLower: 'root', isRoot: true });
|
||||||
|
alice = await createUser({ username: 'alice', usernameLower: 'alice', isRoot: false });
|
||||||
|
bob = await createUser({ username: 'bob', usernameLower: 'bob', isRoot: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await usersRepository.delete({});
|
||||||
|
await userProfilesRepository.delete({});
|
||||||
|
await flashsRepository.delete({});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('featured', () => {
|
||||||
|
test('should return featured flashes', async () => {
|
||||||
|
const flash1 = await createFlash({ likedCount: 1 });
|
||||||
|
const flash2 = await createFlash({ likedCount: 2 });
|
||||||
|
const flash3 = await createFlash({ likedCount: 3 });
|
||||||
|
|
||||||
|
const result = await service.featured({
|
||||||
|
offset: 0,
|
||||||
|
limit: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual([flash3, flash2, flash1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return featured flashes public visibility only', async () => {
|
||||||
|
const flash1 = await createFlash({ likedCount: 1, visibility: 'public' });
|
||||||
|
const flash2 = await createFlash({ likedCount: 2, visibility: 'public' });
|
||||||
|
const flash3 = await createFlash({ likedCount: 3, visibility: 'private' });
|
||||||
|
|
||||||
|
const result = await service.featured({
|
||||||
|
offset: 0,
|
||||||
|
limit: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual([flash2, flash1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return featured flashes with offset', async () => {
|
||||||
|
const flash1 = await createFlash({ likedCount: 1 });
|
||||||
|
const flash2 = await createFlash({ likedCount: 2 });
|
||||||
|
const flash3 = await createFlash({ likedCount: 3 });
|
||||||
|
|
||||||
|
const result = await service.featured({
|
||||||
|
offset: 1,
|
||||||
|
limit: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual([flash2, flash1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return featured flashes with limit', async () => {
|
||||||
|
const flash1 = await createFlash({ likedCount: 1 });
|
||||||
|
const flash2 = await createFlash({ likedCount: 2 });
|
||||||
|
const flash3 = await createFlash({ likedCount: 3 });
|
||||||
|
|
||||||
|
const result = await service.featured({
|
||||||
|
offset: 0,
|
||||||
|
limit: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual([flash3, flash2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,26 +6,33 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i v-if="report.resolved" class="ti ti-check" style="color: var(--success)"></i>
|
<i v-if="report.resolved && report.resolvedAs === 'accept'" class="ti ti-check" style="color: var(--success)"></i>
|
||||||
|
<i v-else-if="report.resolved && report.resolvedAs === 'reject'" class="ti ti-x" style="color: var(--error)"></i>
|
||||||
|
<i v-else-if="report.resolved" class="ti ti-slash"></i>
|
||||||
<i v-else class="ti ti-exclamation-circle" style="color: var(--warn)"></i>
|
<i v-else class="ti ti-exclamation-circle" style="color: var(--warn)"></i>
|
||||||
</template>
|
</template>
|
||||||
<template #label><MkAcct :user="report.targetUser"/> (by <MkAcct :user="report.reporter"/>)</template>
|
<template #label><MkAcct :user="report.targetUser"/> (by <MkAcct :user="report.reporter"/>)</template>
|
||||||
<template #caption>{{ report.comment }}</template>
|
<template #caption>{{ report.comment }}</template>
|
||||||
<template #suffix><MkTime :time="report.createdAt"/></template>
|
<template #suffix><MkTime :time="report.createdAt"/></template>
|
||||||
<template v-if="!report.resolved" #footer>
|
<template #footer>
|
||||||
<div class="_buttons">
|
<div class="_buttons">
|
||||||
<MkButton primary @click="resolve">{{ i18n.ts.abuseMarkAsResolved }}</MkButton>
|
<template v-if="!report.resolved">
|
||||||
<template v-if="report.targetUser.host == null || report.resolved">
|
<MkButton @click="resolve('accept')"><i class="ti ti-check" style="color: var(--success)"></i> {{ i18n.ts._abuseUserReport.resolve }} ({{ i18n.ts._abuseUserReport.accept }})</MkButton>
|
||||||
<MkButton primary @click="resolveAndForward">{{ i18n.ts.forwardReport }}</MkButton>
|
<MkButton @click="resolve('reject')"><i class="ti ti-x" style="color: var(--error)"></i> {{ i18n.ts._abuseUserReport.resolve }} ({{ i18n.ts._abuseUserReport.reject }})</MkButton>
|
||||||
<div v-tooltip:dialog="i18n.ts.forwardReportIsAnonymous" class="_button _help"><i class="ti ti-help-circle"></i></div>
|
<MkButton @click="resolve(null)"><i class="ti ti-slash"></i> {{ i18n.ts._abuseUserReport.resolve }} ({{ i18n.ts.other }})</MkButton>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="report.targetUser.host == null">
|
||||||
|
<MkButton :disabled="report.forwarded" primary @click="forward"><i class="ti ti-corner-up-right"></i> {{ i18n.ts._abuseUserReport.forward }}</MkButton>
|
||||||
|
<div v-tooltip:dialog="i18n.ts._abuseUserReport.forwardDescription" class="_button _help"><i class="ti ti-help-circle"></i></div>
|
||||||
|
</template>
|
||||||
|
<button class="_button" style="margin-left: auto; width: 34px;" @click="showMenu"><i class="ti ti-dots"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div :class="$style.root" class="_gaps_s">
|
<div :class="$style.root" class="_gaps_s">
|
||||||
<MkFolder :withSpacer="false">
|
<MkFolder :withSpacer="false">
|
||||||
<template #icon><MkAvatar :user="report.targetUser" style="width: 18px; height: 18px;"/></template>
|
<template #icon><MkAvatar :user="report.targetUser" style="width: 18px; height: 18px;"/></template>
|
||||||
<template #label>Target: <MkAcct :user="report.targetUser"/></template>
|
<template #label>{{ i18n.ts.target }}: <MkAcct :user="report.targetUser"/></template>
|
||||||
<template #suffix>#{{ report.targetUserId.toUpperCase() }}</template>
|
<template #suffix>#{{ report.targetUserId.toUpperCase() }}</template>
|
||||||
|
|
||||||
<div style="container-type: inline-size;">
|
<div style="container-type: inline-size;">
|
||||||
|
@ -36,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkFolder :defaultOpen="true">
|
<MkFolder :defaultOpen="true">
|
||||||
<template #icon><i class="ti ti-message-2"></i></template>
|
<template #icon><i class="ti ti-message-2"></i></template>
|
||||||
<template #label>{{ i18n.ts.details }}</template>
|
<template #label>{{ i18n.ts.details }}</template>
|
||||||
<div>
|
<div class="_gaps_s">
|
||||||
<Mfm :text="report.comment" :linkNavigationBehavior="'window'"/>
|
<Mfm :text="report.comment" :linkNavigationBehavior="'window'"/>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
@ -51,6 +58,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder :defaultOpen="false">
|
||||||
|
<template #icon><i class="ti ti-message-2"></i></template>
|
||||||
|
<template #label>{{ i18n.ts.moderationNote }}</template>
|
||||||
|
<template #suffix>{{ moderationNote.length > 0 ? '...' : i18n.ts.none }}</template>
|
||||||
|
<div class="_gaps_s">
|
||||||
|
<MkTextarea v-model="moderationNote" manualSave>
|
||||||
|
<template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
|
||||||
|
</MkTextarea>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<div v-if="report.assignee">
|
<div v-if="report.assignee">
|
||||||
{{ i18n.ts.moderator }}:
|
{{ i18n.ts.moderator }}:
|
||||||
<MkAcct :user="report.assignee"/>
|
<MkAcct :user="report.assignee"/>
|
||||||
|
@ -60,7 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { provide, ref } from 'vue';
|
import { provide, ref, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
@ -71,6 +89,8 @@ import { dateString } from '@/filters/date.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import RouterView from '@/components/global/RouterView.vue';
|
import RouterView from '@/components/global/RouterView.vue';
|
||||||
import { useRouterFactory } from '@/router/supplier';
|
import { useRouterFactory } from '@/router/supplier';
|
||||||
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
|
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
report: Misskey.entities.AdminAbuseUserReportsResponse[number];
|
report: Misskey.entities.AdminAbuseUserReportsResponse[number];
|
||||||
|
@ -86,22 +106,48 @@ targetRouter.init();
|
||||||
const reporterRouter = routerFactory(`/admin/user/${props.report.reporterId}`);
|
const reporterRouter = routerFactory(`/admin/user/${props.report.reporterId}`);
|
||||||
reporterRouter.init();
|
reporterRouter.init();
|
||||||
|
|
||||||
function resolve() {
|
const moderationNote = ref(props.report.moderationNote ?? '');
|
||||||
|
|
||||||
|
watch(moderationNote, async () => {
|
||||||
|
os.apiWithDialog('admin/update-abuse-user-report', {
|
||||||
|
reportId: props.report.id,
|
||||||
|
moderationNote: moderationNote.value,
|
||||||
|
}).then(() => {
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function resolve(resolvedAs) {
|
||||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
||||||
reportId: props.report.id,
|
reportId: props.report.id,
|
||||||
|
resolvedAs,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
emit('resolved', props.report.id);
|
emit('resolved', props.report.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveAndForward() {
|
function forward() {
|
||||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
os.apiWithDialog('admin/forward-abuse-user-report', {
|
||||||
forward: true,
|
|
||||||
reportId: props.report.id,
|
reportId: props.report.id,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
emit('resolved', props.report.id);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showMenu(ev: MouseEvent) {
|
||||||
|
os.popupMenu([{
|
||||||
|
icon: 'ti ti-id',
|
||||||
|
text: 'Copy ID',
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(props.report.id);
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
icon: 'ti ti-json',
|
||||||
|
text: 'Copy JSON',
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(JSON.stringify(props.report, null, '\t'));
|
||||||
|
},
|
||||||
|
}], ev.currentTarget ?? ev.target);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:class="[
|
:class="[
|
||||||
$style.root,
|
$style.root,
|
||||||
tail === 'left' ? $style.left : $style.right,
|
tail === 'left' ? $style.left : $style.right,
|
||||||
negativeMargin === true && $style.negativeMergin,
|
negativeMargin === true && $style.negativeMargin,
|
||||||
shadow === true && $style.shadow,
|
shadow === true && $style.shadow,
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
@ -54,7 +54,7 @@ withDefaults(defineProps<{
|
||||||
&.left {
|
&.left {
|
||||||
padding-left: calc(var(--fukidashi-radius) * .13);
|
padding-left: calc(var(--fukidashi-radius) * .13);
|
||||||
|
|
||||||
&.negativeMergin {
|
&.negativeMargin {
|
||||||
margin-left: calc(calc(var(--fukidashi-radius) * .13) * -1);
|
margin-left: calc(calc(var(--fukidashi-radius) * .13) * -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ withDefaults(defineProps<{
|
||||||
&.right {
|
&.right {
|
||||||
padding-right: calc(var(--fukidashi-radius) * .13);
|
padding-right: calc(var(--fukidashi-radius) * .13);
|
||||||
|
|
||||||
&.negativeMergin {
|
&.negativeMargin {
|
||||||
margin-right: calc(calc(var(--fukidashi-radius) * .13) * -1);
|
margin-right: calc(calc(var(--fukidashi-radius) * .13) * -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -437,9 +437,11 @@ onBeforeUnmount(() => {
|
||||||
|
|
||||||
&.big:not(.asDrawer) {
|
&.big:not(.asDrawer) {
|
||||||
> .menu {
|
> .menu {
|
||||||
|
min-width: 230px;
|
||||||
|
|
||||||
> .item {
|
> .item {
|
||||||
padding: 6px 20px;
|
padding: 6px 20px;
|
||||||
font-size: 1em;
|
font-size: 0.95em;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<MkTextarea v-model="moderationNote" manualSave>
|
<MkTextarea v-model="moderationNote" manualSave>
|
||||||
<template #label>{{ i18n.ts.moderationNote }}</template>
|
<template #label>{{ i18n.ts.moderationNote }}</template>
|
||||||
|
<template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
@ -205,6 +206,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, watch, ref } from 'vue';
|
import { computed, defineAsyncComponent, watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { url } from '@@/js/config.js';
|
||||||
import MkChart from '@/components/MkChart.vue';
|
import MkChart from '@/components/MkChart.vue';
|
||||||
import MkObjectView from '@/components/MkObjectView.vue';
|
import MkObjectView from '@/components/MkObjectView.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
|
@ -220,7 +222,6 @@ import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { url } from '@@/js/config.js';
|
|
||||||
import { acct } from '@/filters/user.js';
|
import { acct } from '@/filters/user.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
|
@ -12,6 +12,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton>
|
<MkButton link to="/admin/abuse-report-notification-recipient" primary>{{ i18n.ts.notificationSetting }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<MkInfo v-if="!defaultStore.reactiveState.abusesTutorial.value" closable @close="closeTutorial()">
|
||||||
|
{{ i18n.ts._abuseUserReport.resolveTutorial }}
|
||||||
|
</MkInfo>
|
||||||
|
|
||||||
<div :class="$style.inputs" class="_gaps">
|
<div :class="$style.inputs" class="_gaps">
|
||||||
<MkSelect v-model="state" style="margin: 0; flex: 1;">
|
<MkSelect v-model="state" style="margin: 0; flex: 1;">
|
||||||
<template #label>{{ i18n.ts.state }}</template>
|
<template #label>{{ i18n.ts.state }}</template>
|
||||||
|
@ -56,7 +60,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, shallowRef, ref } from 'vue';
|
import { computed, shallowRef, ref } from 'vue';
|
||||||
|
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
@ -64,6 +67,8 @@ import XAbuseReport from '@/components/MkAbuseReport.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
||||||
const reports = shallowRef<InstanceType<typeof MkPagination>>();
|
const reports = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
|
@ -87,6 +92,10 @@ function resolved(reportId) {
|
||||||
reports.value?.removeItem(reportId);
|
reports.value?.removeItem(reportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeTutorial() {
|
||||||
|
defaultStore.set('abusesTutorial', false);
|
||||||
|
}
|
||||||
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
|
@ -165,6 +165,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
|
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="log.type === 'updateAbuseReportNote'">
|
||||||
|
<div :class="$style.diff">
|
||||||
|
<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>raw</summary>
|
<summary>raw</summary>
|
||||||
|
|
|
@ -55,7 +55,8 @@ const tab = ref('featured');
|
||||||
|
|
||||||
const featuredFlashsPagination = {
|
const featuredFlashsPagination = {
|
||||||
endpoint: 'flash/featured' as const,
|
endpoint: 'flash/featured' as const,
|
||||||
noPaging: true,
|
limit: 5,
|
||||||
|
offsetMode: true,
|
||||||
};
|
};
|
||||||
const myFlashsPagination = {
|
const myFlashsPagination = {
|
||||||
endpoint: 'flash/my' as const,
|
endpoint: 'flash/my' as const,
|
||||||
|
|
|
@ -51,6 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
|
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
|
||||||
<MkTextarea v-model="moderationNote" manualSave>
|
<MkTextarea v-model="moderationNote" manualSave>
|
||||||
<template #label>{{ i18n.ts.moderationNote }}</template>
|
<template #label>{{ i18n.ts.moderationNote }}</template>
|
||||||
|
<template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
|
@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-if="user.followedMessage != null" class="followedMessage">
|
<div v-if="user.followedMessage != null" class="followedMessage">
|
||||||
<MkFukidashi class="fukidashi" :tail="narrow ? 'none' : 'left'" negativeMargin shadow>
|
<MkFukidashi class="fukidashi" :tail="narrow ? 'none' : 'left'" negativeMargin shadow>
|
||||||
<div class="messageHeader">{{ i18n.ts.messageToFollower }}</div>
|
<div class="messageHeader">{{ i18n.ts.messageToFollower }}</div>
|
||||||
<div><Mfm :text="user.followedMessage" :author="user"/></div>
|
<div><MkSparkle><Mfm :text="user.followedMessage" :author="user"/></MkSparkle></div>
|
||||||
</MkFukidashi>
|
</MkFukidashi>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="user.roles.length > 0" class="roles">
|
<div v-if="user.roles.length > 0" class="roles">
|
||||||
|
@ -64,6 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-if="iAmModerator" class="moderationNote">
|
<div v-if="iAmModerator" class="moderationNote">
|
||||||
<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave>
|
<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave>
|
||||||
<template #label>{{ i18n.ts.moderationNote }}</template>
|
<template #label>{{ i18n.ts.moderationNote }}</template>
|
||||||
|
<template #caption>{{ i18n.ts.moderationNoteDescription }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton>
|
<MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton>
|
||||||
|
@ -159,6 +160,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue';
|
import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { getScrollPosition } from '@@/js/scroll.js';
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||||
import MkAccountMoved from '@/components/MkAccountMoved.vue';
|
import MkAccountMoved from '@/components/MkAccountMoved.vue';
|
||||||
|
@ -168,7 +170,6 @@ import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import MkOmit from '@/components/MkOmit.vue';
|
import MkOmit from '@/components/MkOmit.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { getScrollPosition } from '@@/js/scroll.js';
|
|
||||||
import { getUserMenu } from '@/scripts/get-user-menu.js';
|
import { getUserMenu } from '@/scripts/get-user-menu.js';
|
||||||
import number from '@/filters/number.js';
|
import number from '@/filters/number.js';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
|
@ -182,6 +183,7 @@ import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
|
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
|
||||||
import { useRouter } from '@/router/supplier.js';
|
import { useRouter } from '@/router/supplier.js';
|
||||||
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||||
|
import MkSparkle from '@/components/MkSparkle.vue';
|
||||||
|
|
||||||
function calcAge(birthdate: string): number {
|
function calcAge(birthdate: string): number {
|
||||||
const date = new Date(birthdate);
|
const date = new Date(birthdate);
|
||||||
|
@ -472,7 +474,7 @@ onUnmounted(() => {
|
||||||
|
|
||||||
> .fukidashi {
|
> .fukidashi {
|
||||||
display: block;
|
display: block;
|
||||||
--fukidashi-bg: color-mix(in srgb, var(--love), var(--panel) 85%);
|
--fukidashi-bg: color-mix(in srgb, var(--accent), var(--panel) 85%);
|
||||||
--fukidashi-radius: 16px;
|
--fukidashi-radius: 16px;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
global: false,
|
global: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
abusesTutorial: {
|
||||||
|
where: 'account',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
keepCw: {
|
keepCw: {
|
||||||
where: 'account',
|
where: 'account',
|
||||||
default: true,
|
default: true,
|
||||||
|
|
|
@ -213,6 +213,9 @@ type AdminFederationRemoveAllFollowingRequest = operations['admin___federation__
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminFederationUpdateInstanceRequest = operations['admin___federation___update-instance']['requestBody']['content']['application/json'];
|
type AdminFederationUpdateInstanceRequest = operations['admin___federation___update-instance']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AdminForwardAbuseUserReportRequest = operations['admin___forward-abuse-user-report']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminGetIndexStatsResponse = operations['admin___get-index-stats']['responses']['200']['content']['application/json'];
|
type AdminGetIndexStatsResponse = operations['admin___get-index-stats']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
@ -378,6 +381,9 @@ type AdminUnsetUserBannerRequest = operations['admin___unset-user-banner']['requ
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
|
type AdminUnsuspendUserRequest = operations['admin___unsuspend-user']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type AdminUpdateAbuseUserReportRequest = operations['admin___update-abuse-user-report']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
|
type AdminUpdateMetaRequest = operations['admin___update-meta']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
@ -1307,6 +1313,8 @@ declare namespace entities {
|
||||||
AdminResetPasswordRequest,
|
AdminResetPasswordRequest,
|
||||||
AdminResetPasswordResponse,
|
AdminResetPasswordResponse,
|
||||||
AdminResolveAbuseUserReportRequest,
|
AdminResolveAbuseUserReportRequest,
|
||||||
|
AdminForwardAbuseUserReportRequest,
|
||||||
|
AdminUpdateAbuseUserReportRequest,
|
||||||
AdminSendEmailRequest,
|
AdminSendEmailRequest,
|
||||||
AdminServerInfoResponse,
|
AdminServerInfoResponse,
|
||||||
AdminShowModerationLogsRequest,
|
AdminShowModerationLogsRequest,
|
||||||
|
@ -1692,6 +1700,7 @@ declare namespace entities {
|
||||||
FlashCreateRequest,
|
FlashCreateRequest,
|
||||||
FlashCreateResponse,
|
FlashCreateResponse,
|
||||||
FlashDeleteRequest,
|
FlashDeleteRequest,
|
||||||
|
FlashFeaturedRequest,
|
||||||
FlashFeaturedResponse,
|
FlashFeaturedResponse,
|
||||||
FlashLikeRequest,
|
FlashLikeRequest,
|
||||||
FlashShowRequest,
|
FlashShowRequest,
|
||||||
|
@ -1941,6 +1950,9 @@ type FlashCreateResponse = operations['flash___create']['responses']['200']['con
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type FlashDeleteRequest = operations['flash___delete']['requestBody']['content']['application/json'];
|
type FlashDeleteRequest = operations['flash___delete']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type FlashFeaturedRequest = operations['flash___featured']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type FlashFeaturedResponse = operations['flash___featured']['responses']['200']['content']['application/json'];
|
type FlashFeaturedResponse = operations['flash___featured']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
@ -2554,6 +2566,12 @@ type ModerationLog = {
|
||||||
} | {
|
} | {
|
||||||
type: 'resolveAbuseReport';
|
type: 'resolveAbuseReport';
|
||||||
info: ModerationLogPayloads['resolveAbuseReport'];
|
info: ModerationLogPayloads['resolveAbuseReport'];
|
||||||
|
} | {
|
||||||
|
type: 'forwardAbuseReport';
|
||||||
|
info: ModerationLogPayloads['forwardAbuseReport'];
|
||||||
|
} | {
|
||||||
|
type: 'updateAbuseReportNote';
|
||||||
|
info: ModerationLogPayloads['updateAbuseReportNote'];
|
||||||
} | {
|
} | {
|
||||||
type: 'unsetUserAvatar';
|
type: 'unsetUserAvatar';
|
||||||
info: ModerationLogPayloads['unsetUserAvatar'];
|
info: ModerationLogPayloads['unsetUserAvatar'];
|
||||||
|
@ -2593,7 +2611,7 @@ type ModerationLog = {
|
||||||
});
|
});
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient", "deleteAccount", "deletePage", "deleteFlash", "deleteGalleryPost"];
|
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "forwardAbuseReport", "updateAbuseReportNote", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient", "deleteAccount", "deletePage", "deleteFlash", "deleteGalleryPost"];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];
|
type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"name": "misskey-js",
|
"name": "misskey-js",
|
||||||
"version": "2024.10.0-beta.4",
|
"version": "2024.10.0-beta.5",
|
||||||
"description": "Misskey SDK for JavaScript",
|
"description": "Misskey SDK for JavaScript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
|
|
|
@ -691,6 +691,28 @@ declare module '../api.js' {
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
|
||||||
|
*/
|
||||||
|
request<E extends 'admin/forward-abuse-user-report', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
|
||||||
|
*/
|
||||||
|
request<E extends 'admin/update-abuse-user-report', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No description provided.
|
* No description provided.
|
||||||
*
|
*
|
||||||
|
|
|
@ -83,6 +83,8 @@ import type {
|
||||||
AdminResetPasswordRequest,
|
AdminResetPasswordRequest,
|
||||||
AdminResetPasswordResponse,
|
AdminResetPasswordResponse,
|
||||||
AdminResolveAbuseUserReportRequest,
|
AdminResolveAbuseUserReportRequest,
|
||||||
|
AdminForwardAbuseUserReportRequest,
|
||||||
|
AdminUpdateAbuseUserReportRequest,
|
||||||
AdminSendEmailRequest,
|
AdminSendEmailRequest,
|
||||||
AdminServerInfoResponse,
|
AdminServerInfoResponse,
|
||||||
AdminShowModerationLogsRequest,
|
AdminShowModerationLogsRequest,
|
||||||
|
@ -468,6 +470,7 @@ import type {
|
||||||
FlashCreateRequest,
|
FlashCreateRequest,
|
||||||
FlashCreateResponse,
|
FlashCreateResponse,
|
||||||
FlashDeleteRequest,
|
FlashDeleteRequest,
|
||||||
|
FlashFeaturedRequest,
|
||||||
FlashFeaturedResponse,
|
FlashFeaturedResponse,
|
||||||
FlashLikeRequest,
|
FlashLikeRequest,
|
||||||
FlashShowRequest,
|
FlashShowRequest,
|
||||||
|
@ -641,6 +644,8 @@ export type Endpoints = {
|
||||||
'admin/relays/remove': { req: AdminRelaysRemoveRequest; res: EmptyResponse };
|
'admin/relays/remove': { req: AdminRelaysRemoveRequest; res: EmptyResponse };
|
||||||
'admin/reset-password': { req: AdminResetPasswordRequest; res: AdminResetPasswordResponse };
|
'admin/reset-password': { req: AdminResetPasswordRequest; res: AdminResetPasswordResponse };
|
||||||
'admin/resolve-abuse-user-report': { req: AdminResolveAbuseUserReportRequest; res: EmptyResponse };
|
'admin/resolve-abuse-user-report': { req: AdminResolveAbuseUserReportRequest; res: EmptyResponse };
|
||||||
|
'admin/forward-abuse-user-report': { req: AdminForwardAbuseUserReportRequest; res: EmptyResponse };
|
||||||
|
'admin/update-abuse-user-report': { req: AdminUpdateAbuseUserReportRequest; res: EmptyResponse };
|
||||||
'admin/send-email': { req: AdminSendEmailRequest; res: EmptyResponse };
|
'admin/send-email': { req: AdminSendEmailRequest; res: EmptyResponse };
|
||||||
'admin/server-info': { req: EmptyRequest; res: AdminServerInfoResponse };
|
'admin/server-info': { req: EmptyRequest; res: AdminServerInfoResponse };
|
||||||
'admin/show-moderation-logs': { req: AdminShowModerationLogsRequest; res: AdminShowModerationLogsResponse };
|
'admin/show-moderation-logs': { req: AdminShowModerationLogsRequest; res: AdminShowModerationLogsResponse };
|
||||||
|
@ -895,7 +900,7 @@ export type Endpoints = {
|
||||||
'pages/update': { req: PagesUpdateRequest; res: EmptyResponse };
|
'pages/update': { req: PagesUpdateRequest; res: EmptyResponse };
|
||||||
'flash/create': { req: FlashCreateRequest; res: FlashCreateResponse };
|
'flash/create': { req: FlashCreateRequest; res: FlashCreateResponse };
|
||||||
'flash/delete': { req: FlashDeleteRequest; res: EmptyResponse };
|
'flash/delete': { req: FlashDeleteRequest; res: EmptyResponse };
|
||||||
'flash/featured': { req: EmptyRequest; res: FlashFeaturedResponse };
|
'flash/featured': { req: FlashFeaturedRequest; res: FlashFeaturedResponse };
|
||||||
'flash/like': { req: FlashLikeRequest; res: EmptyResponse };
|
'flash/like': { req: FlashLikeRequest; res: EmptyResponse };
|
||||||
'flash/show': { req: FlashShowRequest; res: FlashShowResponse };
|
'flash/show': { req: FlashShowRequest; res: FlashShowResponse };
|
||||||
'flash/unlike': { req: FlashUnlikeRequest; res: EmptyResponse };
|
'flash/unlike': { req: FlashUnlikeRequest; res: EmptyResponse };
|
||||||
|
|
|
@ -86,6 +86,8 @@ export type AdminRelaysRemoveRequest = operations['admin___relays___remove']['re
|
||||||
export type AdminResetPasswordRequest = operations['admin___reset-password']['requestBody']['content']['application/json'];
|
export type AdminResetPasswordRequest = operations['admin___reset-password']['requestBody']['content']['application/json'];
|
||||||
export type AdminResetPasswordResponse = operations['admin___reset-password']['responses']['200']['content']['application/json'];
|
export type AdminResetPasswordResponse = operations['admin___reset-password']['responses']['200']['content']['application/json'];
|
||||||
export type AdminResolveAbuseUserReportRequest = operations['admin___resolve-abuse-user-report']['requestBody']['content']['application/json'];
|
export type AdminResolveAbuseUserReportRequest = operations['admin___resolve-abuse-user-report']['requestBody']['content']['application/json'];
|
||||||
|
export type AdminForwardAbuseUserReportRequest = operations['admin___forward-abuse-user-report']['requestBody']['content']['application/json'];
|
||||||
|
export type AdminUpdateAbuseUserReportRequest = operations['admin___update-abuse-user-report']['requestBody']['content']['application/json'];
|
||||||
export type AdminSendEmailRequest = operations['admin___send-email']['requestBody']['content']['application/json'];
|
export type AdminSendEmailRequest = operations['admin___send-email']['requestBody']['content']['application/json'];
|
||||||
export type AdminServerInfoResponse = operations['admin___server-info']['responses']['200']['content']['application/json'];
|
export type AdminServerInfoResponse = operations['admin___server-info']['responses']['200']['content']['application/json'];
|
||||||
export type AdminShowModerationLogsRequest = operations['admin___show-moderation-logs']['requestBody']['content']['application/json'];
|
export type AdminShowModerationLogsRequest = operations['admin___show-moderation-logs']['requestBody']['content']['application/json'];
|
||||||
|
@ -471,6 +473,7 @@ export type PagesUpdateRequest = operations['pages___update']['requestBody']['co
|
||||||
export type FlashCreateRequest = operations['flash___create']['requestBody']['content']['application/json'];
|
export type FlashCreateRequest = operations['flash___create']['requestBody']['content']['application/json'];
|
||||||
export type FlashCreateResponse = operations['flash___create']['responses']['200']['content']['application/json'];
|
export type FlashCreateResponse = operations['flash___create']['responses']['200']['content']['application/json'];
|
||||||
export type FlashDeleteRequest = operations['flash___delete']['requestBody']['content']['application/json'];
|
export type FlashDeleteRequest = operations['flash___delete']['requestBody']['content']['application/json'];
|
||||||
|
export type FlashFeaturedRequest = operations['flash___featured']['requestBody']['content']['application/json'];
|
||||||
export type FlashFeaturedResponse = operations['flash___featured']['responses']['200']['content']['application/json'];
|
export type FlashFeaturedResponse = operations['flash___featured']['responses']['200']['content']['application/json'];
|
||||||
export type FlashLikeRequest = operations['flash___like']['requestBody']['content']['application/json'];
|
export type FlashLikeRequest = operations['flash___like']['requestBody']['content']['application/json'];
|
||||||
export type FlashShowRequest = operations['flash___show']['requestBody']['content']['application/json'];
|
export type FlashShowRequest = operations['flash___show']['requestBody']['content']['application/json'];
|
||||||
|
|
|
@ -576,6 +576,24 @@ export type paths = {
|
||||||
*/
|
*/
|
||||||
post: operations['admin___resolve-abuse-user-report'];
|
post: operations['admin___resolve-abuse-user-report'];
|
||||||
};
|
};
|
||||||
|
'/admin/forward-abuse-user-report': {
|
||||||
|
/**
|
||||||
|
* admin/forward-abuse-user-report
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
|
||||||
|
*/
|
||||||
|
post: operations['admin___forward-abuse-user-report'];
|
||||||
|
};
|
||||||
|
'/admin/update-abuse-user-report': {
|
||||||
|
/**
|
||||||
|
* admin/update-abuse-user-report
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
|
||||||
|
*/
|
||||||
|
post: operations['admin___update-abuse-user-report'];
|
||||||
|
};
|
||||||
'/admin/send-email': {
|
'/admin/send-email': {
|
||||||
/**
|
/**
|
||||||
* admin/send-email
|
* admin/send-email
|
||||||
|
@ -8721,8 +8739,113 @@ export type operations = {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
reportId: string;
|
reportId: string;
|
||||||
/** @default false */
|
/** @enum {string|null} */
|
||||||
forward?: boolean;
|
resolvedAs?: 'accept' | 'reject' | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (without any results) */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* admin/forward-abuse-user-report
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
|
||||||
|
*/
|
||||||
|
'admin___forward-abuse-user-report': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
reportId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (without any results) */
|
||||||
|
204: {
|
||||||
|
content: never;
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* admin/update-abuse-user-report
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
|
||||||
|
*/
|
||||||
|
'admin___update-abuse-user-report': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** Format: misskey:id */
|
||||||
|
reportId: string;
|
||||||
|
moderationNote?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -23979,6 +24102,16 @@ export type operations = {
|
||||||
* **Credential required**: *No*
|
* **Credential required**: *No*
|
||||||
*/
|
*/
|
||||||
flash___featured: {
|
flash___featured: {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** @default 0 */
|
||||||
|
offset?: number;
|
||||||
|
/** @default 10 */
|
||||||
|
limit?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
responses: {
|
responses: {
|
||||||
/** @description OK (with results) */
|
/** @description OK (with results) */
|
||||||
200: {
|
200: {
|
||||||
|
|
|
@ -142,6 +142,8 @@ export const moderationLogTypes = [
|
||||||
'markSensitiveDriveFile',
|
'markSensitiveDriveFile',
|
||||||
'unmarkSensitiveDriveFile',
|
'unmarkSensitiveDriveFile',
|
||||||
'resolveAbuseReport',
|
'resolveAbuseReport',
|
||||||
|
'forwardAbuseReport',
|
||||||
|
'updateAbuseReportNote',
|
||||||
'createInvitation',
|
'createInvitation',
|
||||||
'createAd',
|
'createAd',
|
||||||
'updateAd',
|
'updateAd',
|
||||||
|
@ -330,7 +332,18 @@ export type ModerationLogPayloads = {
|
||||||
resolveAbuseReport: {
|
resolveAbuseReport: {
|
||||||
reportId: string;
|
reportId: string;
|
||||||
report: ReceivedAbuseReport;
|
report: ReceivedAbuseReport;
|
||||||
forwarded: boolean;
|
forwarded?: boolean;
|
||||||
|
resolvedAs?: string | null;
|
||||||
|
};
|
||||||
|
forwardAbuseReport: {
|
||||||
|
reportId: string;
|
||||||
|
report: ReceivedAbuseReport;
|
||||||
|
};
|
||||||
|
updateAbuseReportNote: {
|
||||||
|
reportId: string;
|
||||||
|
report: ReceivedAbuseReport;
|
||||||
|
before: string;
|
||||||
|
after: string;
|
||||||
};
|
};
|
||||||
createInvitation: {
|
createInvitation: {
|
||||||
invitations: InviteCode[];
|
invitations: InviteCode[];
|
||||||
|
|
|
@ -153,6 +153,12 @@ export type ModerationLog = {
|
||||||
} | {
|
} | {
|
||||||
type: 'resolveAbuseReport';
|
type: 'resolveAbuseReport';
|
||||||
info: ModerationLogPayloads['resolveAbuseReport'];
|
info: ModerationLogPayloads['resolveAbuseReport'];
|
||||||
|
} | {
|
||||||
|
type: 'forwardAbuseReport';
|
||||||
|
info: ModerationLogPayloads['forwardAbuseReport'];
|
||||||
|
} | {
|
||||||
|
type: 'updateAbuseReportNote';
|
||||||
|
info: ModerationLogPayloads['updateAbuseReportNote'];
|
||||||
} | {
|
} | {
|
||||||
type: 'unsetUserAvatar';
|
type: 'unsetUserAvatar';
|
||||||
info: ModerationLogPayloads['unsetUserAvatar'];
|
info: ModerationLogPayloads['unsetUserAvatar'];
|
||||||
|
|
Loading…
Reference in a new issue