Skip to content

Commit

Permalink
Merge branch 'monster' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
jbblily authored Jul 31, 2024
2 parents e98f66d + feb1a0e commit 1958dcf
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 35 deletions.
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- 翻訳の更新
- 依存関係の更新


### Client
- Feat: ユーザーページから「このユーザーのノートを検索」できるように (#14128)
- Feat: 検索ページはクエリを受け付けるようになりました (#14128)
Expand Down Expand Up @@ -280,6 +281,58 @@
- `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正
- Fix: 通知の受信設定で「相互フォロー」が正しく動作しない問題を修正

## 2024.3.1

### General
-

### Client
- Fix: 絵文字関係の不具合を修正 (#13485)
- 履歴に残っている or ピン留めされた絵文字がコントロールパネルより削除されていた際にリアクションデッキが表示できなくなる
- Unicode絵文字が履歴に残っている or ピン留めされているとリアクションデッキが表示できなくなる
- Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487

### Server
-

## 2024.3.0

### General
- Enhance: 投稿者のロールに応じて、一つのノートに含むことのできるメンションとダイレクト投稿の宛先の人数に上限を設定できるように
* デフォルトのメンション上限は20アカウントに設定されます。(管理者はベースロールの設定で変更可能です。)
* 連合の問い合わせに応答しないサーバーのリモートユーザーへのメンションは、上限の人数に含めない実装になっています。
- Enhance: 通知がミュート、凍結を考慮するようになりました
- Enhance: サーバーごとにモデレーションノートを残せるように
- Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加
- Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加
- Enhance: 通知の履歴をリセットできるように
- Fix: ダイレクトなノートに対してはダイレクトでしか返信できないように

### Client
- Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整
- Fix: syuilo/misskeyの時代からあるインスタンスが改変されたバージョンであると誤認識される問題
- Fix: MFMのオートコンプリートが出るべき状況で出ないことがある問題を修正
- Fix: チャートのラベルが消えている問題を修正
- Fix: 画面表示後最初の音声再生が爆音になることがある問題を修正
- Fix: 設定のバックアップ作成時に名前を入力しなかった場合、ローカライゼーションがおかしくなる問題を修正
- Fix: ページ`/admin/emojis`の絵文字編集ダイアログで「リアクションとして使えるロール」を追加する際に何も選択せずOKを押下すると画面が固まる問題を修正
- Fix: 絵文字サジェストの順位で、絵文字自体の名前が同じものよりもタグで一致しているものが優先されてしまう問題を修正
- Fix: ユーザの情報のポップアップが消えなくなることがある問題を修正

### Server
- Enhance: エンドポイント`flash/update``flashId`以外のパラメータは必須ではなくなりました
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
- Fix: 破損した通知をクライアントに送信しないように
* 通知欄が無限にリロードされる問題が改善する可能性があります
- Fix: 禁止キーワードを含むノートがDelayed Queueに追加されて再処理される問題を修正
- Fix: 自分がフォローしていないアカウントのフォロワー限定ノートが閲覧できることがある問題を修正
- Fix: タイムラインのオプションで「リノートを表示」を無効にしている際、投票のみの引用リノートが流れてこない問題を修正
- Fix: エンドポイント`admin/emoji/update`の各種修正
- 必須パラメータを`id`または`name`のいずれかのみに
- `id`の代わりに`name`で絵文字を指定可能に(`id``name`両指定時は従来通り`name`を変更する挙動)
- `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正
- Fix: 通知の受信設定で「相互フォロー」が正しく動作しない問題を修正

## 2024.2.0

### Note
Expand Down
13 changes: 13 additions & 0 deletions packages/backend/migration/1699432324194-remoteAvaterDecoration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class RemoteAvaterDecoration1699432324194 {
name = 'RemoteAvaterDecoration1699432324194'

async up(queryRunner) {
queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "remoteId" varchar(32)`);
queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "host" varchar(128)`);
}

async down(queryRunner) {
queryRunner.query(`ALTER TABLE "avatar_decoration" DROP COLUMN "host"`);
queryRunner.query(`ALTER TABLE "avatar_decoration" DROP COLUMN "remoteId"`);
}
}
120 changes: 117 additions & 3 deletions packages/backend/src/core/AvatarDecorationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,47 @@

import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import * as Redis from 'ioredis';
import type { AvatarDecorationsRepository, MiAvatarDecoration, MiUser } from '@/models/_.js';
import type { AvatarDecorationsRepository, InstancesRepository, UsersRepository, MiAvatarDecoration, MiUser } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { MemorySingleCache } from '@/misc/cache.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { HttpRequestService } from "@/core/HttpRequestService.js";
import { appendQuery, query } from '@/misc/prelude/url.js';
import type { Config } from '@/config.js';
import {IsNull} from "typeorm";

@Injectable()
export class AvatarDecorationService implements OnApplicationShutdown {
public cache: MemorySingleCache<MiAvatarDecoration[]>;
public cacheWithRemote: MemorySingleCache<MiAvatarDecoration[]>;

constructor(
@Inject(DI.config)
private config: Config,

@Inject(DI.redisForSub)
private redisForSub: Redis.Redis,

@Inject(DI.avatarDecorationsRepository)
private avatarDecorationsRepository: AvatarDecorationsRepository,

@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,

@Inject(DI.usersRepository)
private usersRepository: UsersRepository,

private idService: IdService,
private moderationLogService: ModerationLogService,
private globalEventService: GlobalEventService,
private httpRequestService: HttpRequestService,
) {
this.cache = new MemorySingleCache<MiAvatarDecoration[]>(1000 * 60 * 30);
this.cacheWithRemote = new MemorySingleCache<MiAvatarDecoration[]>(1000 * 60 * 30);

this.redisForSub.on('message', this.onMessage);
}
Expand Down Expand Up @@ -94,6 +110,99 @@ export class AvatarDecorationService implements OnApplicationShutdown {
}
}

@bindThis
private getProxiedUrl(url: string, mode?: 'static' | 'avatar'): string {
return appendQuery(
`${this.config.mediaProxy}/${mode ?? 'image'}.webp`,
query({
url,
...(mode ? { [mode]: '1' } : {}),
}),
);
}

@bindThis
public async remoteUserUpdate(user: MiUser) {
const userHost = user.host ?? '';
const instance = await this.instancesRepository.findOneBy({ host: userHost });
const userHostUrl = `https://${user.host}`;
const showUserApiUrl = `${userHostUrl}/api/users/show`;

if (instance?.softwareName !== 'misskey' && instance?.softwareName !== 'cherrypick') {
return;
}

const res = await this.httpRequestService.send(showUserApiUrl, {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ "username": user.username }),
});

const userData: any = await res.json();
const userAvatarDecorations = userData.avatarDecorations ?? undefined;

if (!userAvatarDecorations || userAvatarDecorations.length === 0) {
const updates = {} as Partial<MiUser>;
updates.avatarDecorations = [];
await this.usersRepository.update({id: user.id}, updates);
return;
}

const instanceHost = instance?.host;
const decorationApiUrl = `https://${instanceHost}/api/get-avatar-decorations`;
const allRes = await this.httpRequestService.send(decorationApiUrl, {
method: 'POST',
headers: {"Content-Type": "application/json"},
body: JSON.stringify({}),
});
const allDecorations: any = await allRes.json();
const updates = {} as Partial<MiUser>;
updates.avatarDecorations = [];
for (const avatarDecoration of userAvatarDecorations) {
let name;
let description;
const avatarDecorationId = avatarDecoration.id
for (const decoration of allDecorations) {
if (decoration.id == avatarDecorationId) {
name = decoration.name;
description = decoration.description;
break;
}
}
const existingDecoration = await this.avatarDecorationsRepository.findOneBy({
host: userHost,
remoteId: avatarDecorationId
});
const decorationData = {
name: name,
description: description,
url: this.getProxiedUrl(avatarDecoration.url, 'static'),
remoteId: avatarDecorationId,
host: userHost,
};
if (existingDecoration == null) {
await this.create(decorationData);
this.cacheWithRemote.delete();
} else {
await this.update(existingDecoration.id, decorationData);
this.cacheWithRemote.delete();
}
const findDecoration = await this.avatarDecorationsRepository.findOneBy({
host: userHost,
remoteId: avatarDecorationId
});

updates.avatarDecorations.push({
id: findDecoration?.id ?? '',
angle: avatarDecoration.angle ?? 0,
flipH: avatarDecoration.flipH ?? false,
offsetX: avatarDecoration.offsetX ?? 0,
offsetY: avatarDecoration.offsetY ?? 0,
});
}
await this.usersRepository.update({id: user.id}, updates);
}

@bindThis
public async delete(id: MiAvatarDecoration['id'], moderator?: MiUser): Promise<void> {
const avatarDecoration = await this.avatarDecorationsRepository.findOneByOrFail({ id });
Expand All @@ -110,11 +219,16 @@ export class AvatarDecorationService implements OnApplicationShutdown {
}

@bindThis
public async getAll(noCache = false): Promise<MiAvatarDecoration[]> {
public async getAll(noCache = false, withRemote = false): Promise<MiAvatarDecoration[]> {
if (noCache) {
this.cache.delete();
this.cacheWithRemote.delete();
}
if (!withRemote) {
return this.cache.fetch(() => this.avatarDecorationsRepository.find({ where: { host: IsNull() } }));
} else {
return this.cacheWithRemote.fetch(() => this.avatarDecorationsRepository.find());
}
return this.cache.fetch(() => this.avatarDecorationsRepository.find());
}

@bindThis
Expand Down
11 changes: 8 additions & 3 deletions packages/backend/src/core/RelayService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,18 @@ export class RelayService {
return JSON.stringify(result);
}

@bindThis
public async getAcceptedRelays(): Promise<MiRelay[]> {
return this.relaysCache.fetch(() => this.relaysRepository.findBy({
status: 'accepted',
}));
}

@bindThis
public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
if (activity == null) return;

const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({
status: 'accepted',
}));
const relays = await this.getAcceptedRelays();
if (relays.length === 0) return;

const copy = deepClone(activity);
Expand Down
39 changes: 15 additions & 24 deletions packages/backend/src/core/UserSuspendService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { Inject, Injectable } from '@nestjs/common';
import { Not, IsNull } from 'typeorm';
import type { FollowingsRepository } from '@/models/_.js';
import type { UsersRepository } from '@/models/_.js';
import type { MiUser } from '@/models/User.js';
import { QueueService } from '@/core/QueueService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
Expand All @@ -17,9 +17,8 @@ import { bindThis } from '@/decorators.js';
@Injectable()
export class UserSuspendService {
constructor(
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,

@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private userEntityService: UserEntityService,
private queueService: QueueService,
private globalEventService: GlobalEventService,
Expand All @@ -37,18 +36,14 @@ export class UserSuspendService {

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);
const inboxes = await this.usersRepository.createQueryBuilder('user')
.select('user.sharedInbox')
.distinctOn(['user.sharedInbox'])
.where('user.sharedInbox IS NOT NULL')
.getMany();

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

for (const inbox of queue) {
Expand All @@ -67,18 +62,14 @@ export class UserSuspendService {

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);
const inboxes = await this.usersRepository.createQueryBuilder('user')
.select('user.sharedInbox')
.distinctOn(['user.sharedInbox'])
.where('user.sharedInbox IS NOT NULL')
.getMany();

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

for (const inbox of queue) {
Expand Down
16 changes: 14 additions & 2 deletions packages/backend/src/core/activitypub/ApInboxService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,17 @@ export class ApInboxService {
const meta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) return;

const relays = await this.relayService.getAcceptedRelays();
const fromRelay = !!actor.inbox && relays.map(r => r.inbox).includes(actor.inbox);
const targetUri = getApId(activity.object);

const unlock = await this.appLockService.getApLock(uri);

try {
// 既に同じURIを持つものが登録されていないかチェック
const exist = await this.apNoteService.fetchNote(uri);
const exist = await this.apNoteService.fetchNote(fromRelay ? targetUri : uri);
if (exist) {
this.logger.info(`Skip existing Note announce (fromRelay: ${fromRelay}, uri:${ fromRelay ? targetUri : uri})`);
return;
}

Expand All @@ -319,7 +324,14 @@ export class ApInboxService {
}

if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) {
return 'skip: invalid actor for this activity';
this.logger.warn('skip: invalid actor for this activity');
return;
}

if (fromRelay) {
const noteObj = await this.noteEntityService.pack(renote, null, { skipHide: true });
this.globalEventService.publishNotesStream(noteObj);
return;
}

this.logger.info(`Creating the (Re)Note: ${uri}`);
Expand Down
Loading

0 comments on commit 1958dcf

Please sign in to comment.