Skip to content

Commit

Permalink
Merge branch 'hanami' into feat-subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
kakkokari-gtyih authored Aug 18, 2024
2 parents e9a3585 + 5cb7a32 commit f1a9011
Show file tree
Hide file tree
Showing 54 changed files with 873 additions and 306 deletions.
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
## Unreleased

### General
-

### Client
-

### Server
-


## 2024.8.0

### General
- Fix: リモートユーザのフォロー・フォロワーの一覧が非公開設定の場合も表示できてしまう問題を修正
- Enhance: モデレーターはすべてのユーザーのフォロー・フォロワーの一覧を見られるように
- Enhance: アカウントの削除のモデレーションログを残すように
- Enhance: 不適切なページ、ギャラリー、Playを管理者権限で削除できるように
- Fix: リモートユーザのフォロー・フォロワーの一覧が非公開設定の場合も表示できてしまう問題を修正

### Client
- Enhance: 「自分のPlay」ページにおいてPlayが非公開かどうかが一目でわかるように
- Enhance: 不適切なページ、ギャラリー、Playを通報できるように
- Fix: Play編集時に公開範囲が「パブリック」にリセットされる問題を修正
- Fix: ページ遷移に失敗することがある問題を修正
- Fix: iOSでユーザー名などがリンクとして誤検知される現象を抑制
- Fix: mCaptchaを使用していてもbotプロテクションに関する警告が消えないのを修正
- Fix: ユーザーのモデレーションページにおいてユーザー名にドットが入っているとシステムアカウントとして表示されてしまう問題を修正
- Fix: 特定の条件下でノートの削除ボタンが出ないのを修正

### Server
- Enhance: 照会時にURLがhtmlかつheadタグ内に`rel="alternate"`, `type="application/activity+json"``link`タグがある場合に追ってリンク先を照会できるように
- Enhance: 凍結されたアカウントのフォローリクエストを表示しないように
- Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374
- 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。
- これにより、プッシュ通知が有効な同条件下の環境において、プッシュ通知が常に発生してしまう問題も修正されます。
Expand All @@ -24,6 +42,11 @@
- Fix: ActivityPubのエンティティタイプ判定で不明なタイプを受け取った場合でも処理を継続するように
- キュー処理のつまりが改善される可能性があります
- Fix: リバーシの対局設定の変更が反映されないのを修正
- Fix: 無制限にストリーミングのチャンネルに接続できる問題を修正
- Fix: ベースロールのポリシーを変更した際にモデログに記録されないのを修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/700)
- Fix: Prevent memory leak from memory caches (#14310)
- Fix: More reliable memory cache eviction (#14311)

## 2024.7.0

Expand Down
9 changes: 7 additions & 2 deletions locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ addAccount: "Add account"
reloadAccountsList: "Reload account list"
loginFailed: "Failed to sign in"
showOnRemote: "View on remote instance"
continueOnRemote: "リモートで続行"
continueOnRemote: "Continue on a remote server"
chooseServerOnMisskeyHub: "Choose a server from the Misskey Hub"
specifyServerHost: "Specify a server host directly"
inputHostName: "Enter the domain"
Expand Down Expand Up @@ -491,7 +491,7 @@ noMessagesYet: "No messages yet"
newMessageExists: "There are new messages"
onlyOneFileCanBeAttached: "You can only attach one file to a message"
signinRequired: "Please register or sign in before continuing"
signinOrContinueOnRemote: "To continue, you need to move your server or sign up / log in to this server."
signinOrContinueOnRemote: "To continue, you need to move your server or sign up / log in to this server."
invitations: "Invites"
invitationCode: "Invitation code"
checking: "Checking..."
Expand Down Expand Up @@ -2325,6 +2325,7 @@ _pages:
eyeCatchingImageSet: "Set thumbnail"
eyeCatchingImageRemove: "Delete thumbnail"
chooseBlock: "Add a block"
enterSectionTitle: "Enter a section title"
selectType: "Select a type"
contentBlocks: "Content"
inputBlocks: "Input"
Expand Down Expand Up @@ -2508,6 +2509,10 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "Create a recipient for abuse reports"
updateAbuseReportNotificationRecipient: "Update recipients for abuse reports"
deleteAbuseReportNotificationRecipient: "Delete a recipient for abuse reports"
deleteAccount: "Delete the account"
deletePage: "Delete the page"
deleteFlash: "Delete Play"
deleteGalleryPost: "Delete the gallery post"
_fileViewer:
title: "File details"
type: "File type"
Expand Down
1 change: 1 addition & 0 deletions locales/es-ES.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ copyFileId: "Copiar ID del archivo"
copyFolderId: "Copiar ID de carpeta"
copyProfileUrl: "Copiar la URL del perfil"
searchUser: "Buscar un usuario"
searchThisUsersNotes: ""
reply: "Responder"
loadMore: "Ver más"
showMore: "Ver más"
Expand Down
18 changes: 17 additions & 1 deletion locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2845,7 +2845,7 @@ export interface Locale extends ILocale {
*/
"reportAbuseOf": ParameterizedString<"name">;
/**
* 通報理由の詳細を記入してください。対象のノートがある場合はそのURLも記入してください
* 通報理由の詳細を記入してください。対象のノートやページなどがある場合はそのURLも記入してください
*/
"fillAbuseReportDescription": string;
/**
Expand Down Expand Up @@ -9802,6 +9802,22 @@ export interface Locale extends ILocale {
* 通報の通知先を削除
*/
"deleteAbuseReportNotificationRecipient": string;
/**
* アカウントを削除
*/
"deleteAccount": string;
/**
* ページを削除
*/
"deletePage": string;
/**
* Playを削除
*/
"deleteFlash": string;
/**
* ギャラリーの投稿を削除
*/
"deleteGalleryPost": string;
};
"_fileViewer": {
/**
Expand Down
6 changes: 5 additions & 1 deletion locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ abuseReports: "通報"
reportAbuse: "通報"
reportAbuseRenote: "リノートを通報"
reportAbuseOf: "{name}を通報する"
fillAbuseReportDescription: "通報理由の詳細を記入してください。対象のノートがある場合はそのURLも記入してください"
fillAbuseReportDescription: "通報理由の詳細を記入してください。対象のノートやページなどがある場合はそのURLも記入してください"
abuseReported: "内容が送信されました。ご報告ありがとうございました。"
reporter: "通報者"
reporteeOrigin: "通報先"
Expand Down Expand Up @@ -2599,6 +2599,10 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "通報の通知先を作成"
updateAbuseReportNotificationRecipient: "通報の通知先を更新"
deleteAbuseReportNotificationRecipient: "通報の通知先を削除"
deleteAccount: "アカウントを削除"
deletePage: "ページを削除"
deleteFlash: "Playを削除"
deleteGalleryPost: "ギャラリーの投稿を削除"

_fileViewer:
title: "ファイルの詳細"
Expand Down
2 changes: 1 addition & 1 deletion locales/pt-PT.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1675,7 +1675,7 @@ _role:
descriptionOfPermission: "<b>Moderador</b> permite que você execute operações básicas relacionadas à moderação.\n<b>Administradores</b> podem alterar todas as configurações do servidor."
assignTarget: "Atribuir"
descriptionOfAssignTarget: "<b>Manual</b> para gerenciar manualmente quem está incluído neste cargo.\n<b>Condicional</b> define uma condição e os usuários que corresponderem a ela serão incluídos automaticamente."
manual: "Documentação"
manual: "Manual"
manualRoles: "Cargos manuais"
conditional: "Condicional"
conditionalRoles: "Cargos condicionais"
Expand Down
5 changes: 5 additions & 0 deletions locales/zh-CN.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,7 @@ _pages:
eyeCatchingImageSet: "设置封面图片"
eyeCatchingImageRemove: "删除封面图片"
chooseBlock: "添加块"
enterSectionTitle: "输入会话标题"
selectType: "选择类型"
contentBlocks: "内容"
inputBlocks: "输入"
Expand Down Expand Up @@ -2499,6 +2500,10 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "新建了举报通知"
updateAbuseReportNotificationRecipient: "更新了举报通知"
deleteAbuseReportNotificationRecipient: "删除了举报通知"
deleteAccount: "删除了账户"
deletePage: "删除了页面"
deleteFlash: "删除了 Play"
deleteGalleryPost: "删除了图库稿件"
_fileViewer:
title: "文件信息"
type: "文件类型"
Expand Down
5 changes: 5 additions & 0 deletions locales/zh-TW.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,7 @@ _pages:
eyeCatchingImageSet: "設定封面影像"
eyeCatchingImageRemove: "刪除封面影像"
chooseBlock: "新增方塊"
enterSectionTitle: "輸入區段的標題"
selectType: "選擇類型"
contentBlocks: "內容"
inputBlocks: "輸入"
Expand Down Expand Up @@ -2499,6 +2500,10 @@ _moderationLogTypes:
createAbuseReportNotificationRecipient: "建立接收檢舉的通知對象"
updateAbuseReportNotificationRecipient: "更新接收檢舉的通知對象"
deleteAbuseReportNotificationRecipient: "刪除接收檢舉的通知對象"
deleteAccount: "刪除帳戶"
deletePage: "刪除頁面"
deleteFlash: "刪除 Play"
deleteGalleryPost: "刪除相簿的貼文"
_fileViewer:
title: "檔案詳細資訊"
type: "檔案類型 "
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2024.8.0-alpha.0",
"version": "2024.8.0",
"codename": "nasubi",
"repository": {
"type": "git",
Expand Down Expand Up @@ -61,7 +61,7 @@
"glob": "11.0.0"
},
"devDependencies": {
"@misskey-dev/eslint-plugin": "2.0.2",
"@misskey-dev/eslint-plugin": "2.0.3",
"@types/node": "20.14.12",
"@typescript-eslint/eslint-plugin": "7.17.0",
"@typescript-eslint/parser": "7.17.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/core/AvatarDecorationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class AvatarDecorationService implements OnApplicationShutdown {
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
) {
this.cache = new MemorySingleCache<MiAvatarDecoration[]>(1000 * 60 * 30);
this.cache = new MemorySingleCache<MiAvatarDecoration[]>(1000 * 60 * 30); // 30s

this.redisForSub.on('message', this.onMessage);
}
Expand Down
12 changes: 6 additions & 6 deletions packages/backend/src/core/CacheService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export class CacheService implements OnApplicationShutdown {
) {
//this.onMessage = this.onMessage.bind(this);

this.userByIdCache = new MemoryKVCache<MiUser>(Infinity);
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null>(Infinity);
this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(Infinity);
this.uriPersonCache = new MemoryKVCache<MiUser | null>(Infinity);
this.userByIdCache = new MemoryKVCache<MiUser>(1000 * 60 * 5); // 5m
this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null>(1000 * 60 * 5); // 5m
this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 5); // 5m
this.uriPersonCache = new MemoryKVCache<MiUser | null>(1000 * 60 * 5); // 5m

this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', {
lifetime: 1000 * 60 * 30, // 30m
Expand Down Expand Up @@ -135,14 +135,14 @@ export class CacheService implements OnApplicationShutdown {
if (user == null) {
this.userByIdCache.delete(body.id);
this.localUserByIdCache.delete(body.id);
for (const [k, v] of this.uriPersonCache.cache.entries()) {
for (const [k, v] of this.uriPersonCache.entries) {
if (v.value?.id === body.id) {
this.uriPersonCache.delete(k);
}
}
} else {
this.userByIdCache.set(user.id, user);
for (const [k, v] of this.uriPersonCache.cache.entries()) {
for (const [k, v] of this.uriPersonCache.entries) {
if (v.value?.id === user.id) {
this.uriPersonCache.set(k, user);
}
Expand Down
12 changes: 6 additions & 6 deletions packages/backend/src/core/CustomEmojiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const parseEmojiStrRegexp = /^([-\w]+)(?:@([\w.-]+))?$/;

@Injectable()
export class CustomEmojiService implements OnApplicationShutdown {
private cache: MemoryKVCache<MiEmoji | null>;
private emojisCache: MemoryKVCache<MiEmoji | null>;
public localEmojisCache: RedisSingleCache<Map<string, MiEmoji>>;

constructor(
Expand All @@ -40,7 +40,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
) {
this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12);
this.emojisCache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12); // 12h

this.localEmojisCache = new RedisSingleCache<Map<string, MiEmoji>>(this.redisClient, 'localEmojis', {
lifetime: 1000 * 60 * 30, // 30m
Expand Down Expand Up @@ -334,7 +334,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
host,
})) ?? null;

const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull);
const emoji = await this.emojisCache.fetch(`${name} ${host}`, queryOrNull);

if (emoji == null) return null;
return emoji.publicUrl || emoji.originalUrl; // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
Expand All @@ -361,7 +361,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
*/
@bindThis
public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> {
const notCachedEmojis = emojis.filter(emoji => this.cache.get(`${emoji.name} ${emoji.host}`) == null);
const notCachedEmojis = emojis.filter(emoji => this.emojisCache.get(`${emoji.name} ${emoji.host}`) == null);
const emojisQuery: any[] = [];
const hosts = new Set(notCachedEmojis.map(e => e.host));
for (const host of hosts) {
Expand All @@ -376,7 +376,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
select: ['name', 'host', 'originalUrl', 'publicUrl'],
}) : [];
for (const emoji of _emojis) {
this.cache.set(`${emoji.name} ${emoji.host}`, emoji);
this.emojisCache.set(`${emoji.name} ${emoji.host}`, emoji);
}
}

Expand All @@ -401,7 +401,7 @@ export class CustomEmojiService implements OnApplicationShutdown {

@bindThis
public dispose(): void {
this.cache.dispose();
this.emojisCache.dispose();
}

@bindThis
Expand Down
60 changes: 52 additions & 8 deletions packages/backend/src/core/DeleteAccountService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,83 @@
*/

import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository } from '@/models/_.js';
import { Not, IsNull } from 'typeorm';
import type { FollowingsRepository, MiUser, UsersRepository } from '@/models/_.js';
import { QueueService } from '@/core/QueueService.js';
import { UserSuspendService } from '@/core/UserSuspendService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';

@Injectable()
export class DeleteAccountService {
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,

private userSuspendService: UserSuspendService,
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,

private userEntityService: UserEntityService,
private apRendererService: ApRendererService,
private queueService: QueueService,
private globalEventService: GlobalEventService,
private moderationLogService: ModerationLogService,
) {
}

@bindThis
public async deleteAccount(user: {
id: string;
host: string | null;
}): Promise<void> {
}, moderator?: MiUser): Promise<void> {
const _user = await this.usersRepository.findOneByOrFail({ id: user.id });
if (_user.isRoot) throw new Error('cannot delete a root account');

if (moderator != null) {
this.moderationLogService.log(moderator, 'deleteAccount', {
userId: user.id,
userUsername: _user.username,
userHost: user.host,
});
}

// 物理削除する前にDelete activityを送信する
await this.userSuspendService.doPostSuspend(user).catch(e => {});
if (this.userEntityService.isLocalUser(user)) {
// 知り得る全SharedInboxにDelete配信
const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.userEntityService.genLocalUserUri(user.id), user));

this.queueService.createDeleteAccountJob(user, {
soft: false,
});
const queue: string[] = [];

const followings = await this.followingsRepository.find({
where: [
{ followerSharedInbox: Not(IsNull()) },
{ followeeSharedInbox: Not(IsNull()) },
],
select: ['followerSharedInbox', 'followeeSharedInbox'],
});

const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox);

for (const inbox of inboxes) {
if (inbox != null && !queue.includes(inbox)) queue.push(inbox);
}

for (const inbox of queue) {
this.queueService.deliver(user, content, inbox, true);
}

this.queueService.createDeleteAccountJob(user, {
soft: false,
});
} else {
// リモートユーザーの削除は、完全にDBから物理削除してしまうと再度連合してきてアカウントが復活する可能性があるため、soft指定する
this.queueService.createDeleteAccountJob(user, {
soft: true,
});
}

await this.usersRepository.update(user.id, {
isDeleted: true,
Expand Down
Loading

0 comments on commit f1a9011

Please sign in to comment.