summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-04-06 19:48:24 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2023-04-06 19:48:24 +0900
commit521eb95dbc89d1825867f9ab4dc3510cbd504ba9 (patch)
treeb20c61ecb3ccc2dccc8200b4726e32acad8fc3de
parentperf(backend): reduce db query (diff)
downloadsharkey-521eb95dbc89d1825867f9ab4dc3510cbd504ba9.tar.gz
sharkey-521eb95dbc89d1825867f9ab4dc3510cbd504ba9.tar.bz2
sharkey-521eb95dbc89d1825867f9ab4dc3510cbd504ba9.zip
perf(backend): cache avatar and banner url to db
-rw-r--r--packages/backend/migration/1680775031481-avatar-url-and-banner-url.js17
-rw-r--r--packages/backend/src/core/activitypub/models/ApPersonService.ts49
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts2
-rw-r--r--packages/backend/src/core/entities/NotificationEntityService.ts3
-rw-r--r--packages/backend/src/core/entities/UserEntityService.ts57
-rw-r--r--packages/backend/src/models/entities/User.ts20
-rw-r--r--packages/backend/src/server/ServerService.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/notes.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/channels/timeline.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/clips/notes.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/i/update.ts12
-rw-r--r--packages/backend/src/server/api/endpoints/notes.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/children.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/featured.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/global-timeline.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/notes/local-timeline.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/mentions.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/reactions.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/renotes.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/notes/replies.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/search-by-tag.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/search.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/timeline.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/users/notes.ts8
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts12
-rw-r--r--packages/backend/src/server/web/FeedService.ts2
28 files changed, 126 insertions, 183 deletions
diff --git a/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js b/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js
new file mode 100644
index 0000000000..7c5fe7ac5e
--- /dev/null
+++ b/packages/backend/migration/1680775031481-avatar-url-and-banner-url.js
@@ -0,0 +1,17 @@
+export class AvatarUrlAndBannerUrl1680775031481 {
+ name = 'AvatarUrlAndBannerUrl1680775031481'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "user" ADD "avatarUrl" character varying(512)`);
+ await queryRunner.query(`ALTER TABLE "user" ADD "bannerUrl" character varying(512)`);
+ await queryRunner.query(`ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`);
+ await queryRunner.query(`ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`);
+ await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`);
+ await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerUrl"`);
+ await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarUrl"`);
+ }
+}
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index 67e907c271..664e7eb4ea 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -31,6 +31,7 @@ import type { UtilityService } from '@/core/UtilityService.js';
import type { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
import { MetaService } from '@/core/MetaService.js';
+import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js';
import { extractApHashtags } from './tag.js';
import type { OnModuleInit } from '@nestjs/common';
@@ -49,6 +50,7 @@ const summaryLength = 2048;
export class ApPersonService implements OnModuleInit {
private utilityService: UtilityService;
private userEntityService: UserEntityService;
+ private driveFileEntityService: DriveFileEntityService;
private idService: IdService;
private globalEventService: GlobalEventService;
private metaService: MetaService;
@@ -113,6 +115,7 @@ export class ApPersonService implements OnModuleInit {
onModuleInit() {
this.utilityService = this.moduleRef.get('UtilityService');
this.userEntityService = this.moduleRef.get('UserEntityService');
+ this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService');
this.idService = this.moduleRef.get('IdService');
this.globalEventService = this.moduleRef.get('GlobalEventService');
this.metaService = this.moduleRef.get('MetaService');
@@ -356,32 +359,44 @@ export class ApPersonService implements OnModuleInit {
const avatarId = avatar ? avatar.id : null;
const bannerId = banner ? banner.id : null;
+ const avatarUrl = avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null;
+ const bannerUrl = banner ? this.driveFileEntityService.getPublicUrl(banner) : null;
+ const avatarBlurhash = avatar ? avatar.blurhash : null;
+ const bannerBlurhash = banner ? banner.blurhash : null;
await this.usersRepository.update(user!.id, {
avatarId,
bannerId,
+ avatarUrl,
+ bannerUrl,
+ avatarBlurhash,
+ bannerBlurhash,
});
- user!.avatarId = avatarId;
- user!.bannerId = bannerId;
- //#endregion
+ user!.avatarId = avatarId;
+ user!.bannerId = bannerId;
+ user!.avatarUrl = avatarUrl;
+ user!.bannerUrl = bannerUrl;
+ user!.avatarBlurhash = avatarBlurhash;
+ user!.bannerBlurhash = bannerBlurhash;
+ //#endregion
- //#region カスタム絵文字取得
- const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => {
- this.logger.info(`extractEmojis: ${err}`);
- return [] as Emoji[];
- });
+ //#region カスタム絵文字取得
+ const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => {
+ this.logger.info(`extractEmojis: ${err}`);
+ return [] as Emoji[];
+ });
- const emojiNames = emojis.map(emoji => emoji.name);
+ const emojiNames = emojis.map(emoji => emoji.name);
- await this.usersRepository.update(user!.id, {
- emojis: emojiNames,
- });
- //#endregion
+ await this.usersRepository.update(user!.id, {
+ emojis: emojiNames,
+ });
+ //#endregion
- await this.updateFeatured(user!.id, resolver).catch(err => this.logger.error(err));
+ await this.updateFeatured(user!.id, resolver).catch(err => this.logger.error(err));
- return user!;
+ return user!;
}
/**
@@ -463,10 +478,14 @@ export class ApPersonService implements OnModuleInit {
if (avatar) {
updates.avatarId = avatar.id;
+ updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
+ updates.avatarBlurhash = avatar.blurhash;
}
if (banner) {
updates.bannerId = banner.id;
+ updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
+ updates.bannerBlurhash = banner.blurhash;
}
// Update user
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 1320e56ca2..f23e0f38ea 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -415,7 +415,7 @@ export class NoteEntityService implements OnModuleInit {
await this.customEmojiService.prefetchEmojis(this.aggregateNoteEmojis(notes));
// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
- const packedFiles = await this.driveFileEntityService.packManyByIdsMap(fileIds);
+ const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
return await Promise.all(notes.map(n => this.pack(n, me, {
...options,
diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index 1948cabad9..0dc63d969f 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -113,7 +113,7 @@ export class NotificationEntityService implements OnModuleInit {
const noteIds = validNotifications.map(x => x.noteId).filter(isNotNull);
const notes = noteIds.length > 0 ? await this.notesRepository.find({
where: { id: In(noteIds) },
- relations: ['user', 'user.avatar', 'user.banner', 'reply', 'reply.user', 'reply.user.avatar', 'reply.user.banner', 'renote', 'renote.user', 'renote.user.avatar', 'renote.user.banner'],
+ relations: ['user', 'reply', 'reply.user', 'renote', 'renote.user'],
}) : [];
const packedNotesArray = await this.noteEntityService.packMany(notes, { id: meId }, {
detail: true,
@@ -125,7 +125,6 @@ export class NotificationEntityService implements OnModuleInit {
const userIds = validNotifications.map(x => x.notifierId).filter(isNotNull);
const users = userIds.length > 0 ? await this.usersRepository.find({
where: { id: In(userIds) },
- relations: ['avatar', 'banner'],
}) : [];
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, {
detail: false,
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index f2f5e4b582..6d8a4dc14e 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -270,27 +270,6 @@ export class UserEntityService implements OnModuleInit {
}
@bindThis
- public async getAvatarUrl(user: User): Promise<string> {
- if (user.avatar) {
- return this.driveFileEntityService.getPublicUrl(user.avatar, 'avatar') ?? this.getIdenticonUrl(user);
- } else if (user.avatarId) {
- const avatar = await this.driveFilesRepository.findOneByOrFail({ id: user.avatarId });
- return this.driveFileEntityService.getPublicUrl(avatar, 'avatar') ?? this.getIdenticonUrl(user);
- } else {
- return this.getIdenticonUrl(user);
- }
- }
-
- @bindThis
- public getAvatarUrlSync(user: User): string {
- if (user.avatar) {
- return this.driveFileEntityService.getPublicUrl(user.avatar, 'avatar') ?? this.getIdenticonUrl(user);
- } else {
- return this.getIdenticonUrl(user);
- }
- }
-
- @bindThis
public getIdenticonUrl(user: User): string {
return `${this.config.url}/identicon/${user.username.toLowerCase()}@${user.host ?? this.config.host}`;
}
@@ -309,19 +288,23 @@ export class UserEntityService implements OnModuleInit {
includeSecrets: false,
}, options);
- let user: User;
+ const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src });
- if (typeof src === 'object') {
- user = src;
- if (src.avatar === undefined && src.avatarId) src.avatar = await this.driveFilesRepository.findOneBy({ id: src.avatarId }) ?? null;
- if (src.banner === undefined && src.bannerId) src.banner = await this.driveFilesRepository.findOneBy({ id: src.bannerId }) ?? null;
- } else {
- user = await this.usersRepository.findOneOrFail({
- where: { id: src },
- relations: {
- avatar: true,
- banner: true,
- },
+ // migration
+ if (user.avatarId != null && user.avatarUrl === null) {
+ const avatar = await this.driveFilesRepository.findOneByOrFail({ id: user.avatarId });
+ user.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
+ this.usersRepository.update(user.id, {
+ avatarUrl: user.avatarUrl,
+ avatarBlurhash: avatar.blurhash,
+ });
+ }
+ if (user.bannerId != null && user.bannerUrl === null) {
+ const banner = await this.driveFilesRepository.findOneByOrFail({ id: user.bannerId });
+ user.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
+ this.usersRepository.update(user.id, {
+ bannerUrl: user.bannerUrl,
+ bannerBlurhash: banner.blurhash,
});
}
@@ -356,8 +339,8 @@ export class UserEntityService implements OnModuleInit {
name: user.name,
username: user.username,
host: user.host,
- avatarUrl: this.getAvatarUrlSync(user),
- avatarBlurhash: user.avatar?.blurhash ?? null,
+ avatarUrl: user.avatarUrl ?? this.getIdenticonUrl(user),
+ avatarBlurhash: user.avatarBlurhash,
isBot: user.isBot ?? falsy,
isCat: user.isCat ?? falsy,
instance: user.host ? this.userInstanceCache.fetch(user.host,
@@ -386,8 +369,8 @@ export class UserEntityService implements OnModuleInit {
createdAt: user.createdAt.toISOString(),
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
- bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner) : null,
- bannerBlurhash: user.banner?.blurhash ?? null,
+ bannerUrl: user.bannerUrl,
+ bannerBlurhash: user.bannerBlurhash,
isLocked: user.isLocked,
isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote),
isSuspended: user.isSuspended ?? falsy,
diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/entities/User.ts
index 0a8b89ea06..ca53c57d5a 100644
--- a/packages/backend/src/models/entities/User.ts
+++ b/packages/backend/src/models/entities/User.ts
@@ -100,6 +100,26 @@ export class User {
@JoinColumn()
public banner: DriveFile | null;
+ @Column('varchar', {
+ length: 512, nullable: true,
+ })
+ public avatarUrl: string | null;
+
+ @Column('varchar', {
+ length: 512, nullable: true,
+ })
+ public bannerUrl: string | null;
+
+ @Column('varchar', {
+ length: 128, nullable: true,
+ })
+ public avatarBlurhash: string | null;
+
+ @Column('varchar', {
+ length: 128, nullable: true,
+ })
+ public bannerBlurhash: string | null;
+
@Index()
@Column('varchar', {
length: 128, array: true, default: '{}',
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 3f116845cb..9257fee13e 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -149,13 +149,12 @@ export class ServerService implements OnApplicationShutdown {
host: (host == null) || (host === this.config.host) ? IsNull() : host,
isSuspended: false,
},
- relations: ['avatar'],
});
reply.header('Cache-Control', 'public, max-age=86400');
if (user) {
- reply.redirect(this.userEntityService.getAvatarUrlSync(user));
+ reply.redirect(user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user));
} else {
reply.redirect('/static-assets/user-unknown.png');
}
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 364f9d9c05..f08c20ae48 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -95,16 +95,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
this.queryService.generateMutedUserQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index eef343d139..dfc2a582dc 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -93,16 +93,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
.leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
.leftJoinAndSelect('note.channel', 'channel');
if (me) {
diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts
index 6818d31cc4..dcb415b752 100644
--- a/packages/backend/src/server/api/endpoints/clips/notes.ts
+++ b/packages/backend/src/server/api/endpoints/clips/notes.ts
@@ -75,16 +75,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.innerJoin(this.clipNotesRepository.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id')
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
.leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
.andWhere('clipNote.clipId = :clipId', { clipId: clip.id });
if (me) {
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index c20f2b7913..be1c72b207 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -19,6 +19,7 @@ import { HashtagService } from '@/core/HashtagService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { CacheService } from '@/core/CacheService.js';
+import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -148,6 +149,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private pagesRepository: PagesRepository,
private userEntityService: UserEntityService,
+ private driveFileEntityService: DriveFileEntityService,
private globalEventService: GlobalEventService,
private userFollowingService: UserFollowingService,
private accountUpdateService: AccountUpdateService,
@@ -170,8 +172,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (ps.location !== undefined) profileUpdates.location = ps.location;
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility;
- if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
- if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
if (ps.mutedWords !== undefined) {
// TODO: ちゃんと数える
const length = JSON.stringify(ps.mutedWords).length;
@@ -217,6 +217,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar);
if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage);
+
+ updates.avatarId = avatar.id;
+ updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
+ updates.avatarBlurhash = avatar.blurhash;
}
if (ps.bannerId) {
@@ -224,6 +228,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner);
if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage);
+
+ updates.bannerId = banner.id;
+ updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
+ updates.bannerBlurhash = banner.blurhash;
}
if (ps.pinnedPageId) {
diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts
index 0a8f2292ac..5fbc7aba58 100644
--- a/packages/backend/src/server/api/endpoints/notes.ts
+++ b/packages/backend/src/server/api/endpoints/notes.ts
@@ -49,16 +49,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.andWhere('note.visibility = \'public\'')
.andWhere('note.localOnly = FALSE')
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
if (ps.local) {
query.andWhere('note.userHost IS NULL');
diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts
index ea7a825f9d..26f2d6772d 100644
--- a/packages/backend/src/server/api/endpoints/notes/children.ts
+++ b/packages/backend/src/server/api/endpoints/notes/children.ts
@@ -57,16 +57,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
}));
}))
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) {
diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts
index 6bf17b222a..bdb06498bc 100644
--- a/packages/backend/src/server/api/endpoints/notes/featured.ts
+++ b/packages/backend/src/server/api/endpoints/notes/featured.ts
@@ -53,16 +53,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.andWhere('note.createdAt > :date', { date: new Date(Date.now() - day) })
.andWhere('note.visibility = \'public\'')
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
if (ps.channelId) query.andWhere('note.channelId = :channelId', { channelId: ps.channelId });
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index 9118d33936..c11c1eac40 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -73,16 +73,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.andWhere('note.visibility = \'public\'')
.andWhere('note.channelId IS NULL')
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateRepliesQuery(query, me);
if (me) {
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 8a7ec65ab4..89abd91c7e 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -91,16 +91,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)');
}))
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
.leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
.setParameters(followingQuery.getParameters());
this.queryService.generateChannelQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 8c1c07a9f4..afdafc7c55 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -80,16 +80,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで
.andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)')
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateChannelQuery(query, me);
this.queryService.generateRepliesQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts
index dcb0d0adcb..4e9f604d8d 100644
--- a/packages/backend/src/server/api/endpoints/notes/mentions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts
@@ -60,16 +60,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`);
}))
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
this.queryService.generateMutedUserQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts
index f758bfe9b1..4772c4f809 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts
@@ -75,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
order: {
id: -1,
},
- relations: ['user', 'user.avatar', 'user.banner', 'note'],
+ relations: ['user', 'note'],
});
return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me)));
diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts
index 026a1baa3e..d406855660 100644
--- a/packages/backend/src/server/api/endpoints/notes/renotes.ts
+++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts
@@ -4,8 +4,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
-import { ApiError } from '../../error.js';
import { GetterService } from '@/server/api/GetterService.js';
+import { ApiError } from '../../error.js';
export const meta = {
tags: ['notes'],
@@ -62,16 +62,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere('note.renoteId = :renoteId', { renoteId: note.id })
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts
index 4df95962c8..f2af71d55f 100644
--- a/packages/backend/src/server/api/endpoints/notes/replies.ts
+++ b/packages/backend/src/server/api/endpoints/notes/replies.ts
@@ -46,16 +46,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere('note.replyId = :replyId', { replyId: ps.noteId })
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
index da1a4bcc46..2956bf1cbd 100644
--- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
@@ -71,16 +71,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
super(meta, paramDef, async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts
index 5db5b6267f..fb5abd917f 100644
--- a/packages/backend/src/server/api/endpoints/notes/search.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search.ts
@@ -85,16 +85,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
query
.andWhere('note.text ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` })
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index d9e72d2603..c6ee1e5c2b 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -70,16 +70,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
if (followees.length > 0) {
const meOrFolloweeIds = [me.id, ...followees.map(f => f.followeeId)];
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 9b23103fd4..afc9bc4213 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -84,16 +84,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.innerJoin(this.userListJoiningsRepository.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId')
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
.leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
.andWhere('userListJoining.userListId = :userListId', { userListId: list.id });
this.queryService.generateVisibilityQuery(query, me);
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index aab32cc58c..aaf94734a3 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -74,16 +74,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.userId = :userId', { userId: user.id })
.innerJoinAndSelect('note.user', 'user')
- .leftJoinAndSelect('user.avatar', 'avatar')
- .leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
- .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
- .leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
- .leftJoinAndSelect('renote.user', 'renoteUser')
- .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
- .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
+ .leftJoinAndSelect('renote.user', 'renoteUser');
this.queryService.generateVisibilityQuery(query, me);
if (me) {
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index fb76f07e48..99ae1b7af6 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -419,7 +419,7 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=15');
return await reply.view('user', {
user, profile, me,
- avatarUrl: await this.userEntityService.getAvatarUrl(user),
+ avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
sub: request.params.sub,
instanceName: meta.name ?? 'Misskey',
icon: meta.iconUrl,
@@ -464,7 +464,7 @@ export class ClientServerService {
return await reply.view('note', {
note: _note,
profile,
- avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: note.userId })),
+ avatarUrl: _note.user.avatarUrl,
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
instanceName: meta.name ?? 'Misskey',
@@ -503,7 +503,7 @@ export class ClientServerService {
return await reply.view('page', {
page: _page,
profile,
- avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: page.userId })),
+ avatarUrl: _page.user.avatarUrl,
instanceName: meta.name ?? 'Misskey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
@@ -527,7 +527,7 @@ export class ClientServerService {
return await reply.view('flash', {
flash: _flash,
profile,
- avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: flash.userId })),
+ avatarUrl: _flash.user.avatarUrl,
instanceName: meta.name ?? 'Misskey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
@@ -551,7 +551,7 @@ export class ClientServerService {
return await reply.view('clip', {
clip: _clip,
profile,
- avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: clip.userId })),
+ avatarUrl: _clip.user.avatarUrl,
instanceName: meta.name ?? 'Misskey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
@@ -573,7 +573,7 @@ export class ClientServerService {
return await reply.view('gallery-post', {
post: _post,
profile,
- avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: post.userId })),
+ avatarUrl: _post.user.avatarUrl,
instanceName: meta.name ?? 'Misskey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts
index a14609adf9..0c0e92cc04 100644
--- a/packages/backend/src/server/web/FeedService.ts
+++ b/packages/backend/src/server/web/FeedService.ts
@@ -58,7 +58,7 @@ export class FeedService {
generator: 'Misskey',
description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`,
link: author.link,
- image: await this.userEntityService.getAvatarUrl(user),
+ image: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
feedLinks: {
json: `${author.link}.json`,
atom: `${author.link}.atom`,