From e314be5b59dc5b518f114c82a67a8546decb5a80 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 27 Feb 2022 13:59:10 +0900 Subject: [PATCH] Fix avatar/banner proxy (#8346) * Fix avatar/banner proxy Co-authored-by: mei23 * use getAvatarUrl * fix * join avatar and banner to improve performance * join * Update hybrid-timeline.ts * fix Co-authored-by: mei23 Co-authored-by: syuilo --- .config/example.yml | 3 ++ locales/ja-JP.yml | 2 -- .../1626509500668-fix-remote-file-proxy.js | 23 +++++++++++++ packages/backend/src/config/types.ts | 1 + packages/backend/src/models/entities/meta.ts | 5 --- packages/backend/src/models/entities/user.ts | 20 ------------ .../src/models/repositories/drive-file.ts | 8 ++--- .../src/models/repositories/note-reaction.ts | 4 +-- .../backend/src/models/repositories/user.ts | 32 ++++++++++++++----- .../src/remote/activitypub/models/person.ts | 16 ---------- .../server/api/endpoints/admin/update-meta.ts | 5 --- .../server/api/endpoints/antennas/notes.ts | 6 ++++ .../server/api/endpoints/channels/timeline.ts | 6 ++++ .../src/server/api/endpoints/clips/notes.ts | 6 ++++ .../server/api/endpoints/i/notifications.ts | 8 ++++- .../src/server/api/endpoints/i/update.ts | 12 ------- .../backend/src/server/api/endpoints/meta.ts | 5 --- .../backend/src/server/api/endpoints/notes.ts | 8 ++++- .../server/api/endpoints/notes/children.ts | 8 ++++- .../server/api/endpoints/notes/featured.ts | 8 ++++- .../api/endpoints/notes/global-timeline.ts | 8 ++++- .../api/endpoints/notes/hybrid-timeline.ts | 6 ++++ .../api/endpoints/notes/local-timeline.ts | 8 ++++- .../server/api/endpoints/notes/mentions.ts | 8 ++++- .../server/api/endpoints/notes/reactions.ts | 1 + .../src/server/api/endpoints/notes/renotes.ts | 8 ++++- .../src/server/api/endpoints/notes/replies.ts | 8 ++++- .../api/endpoints/notes/search-by-tag.ts | 8 ++++- .../src/server/api/endpoints/notes/search.ts | 8 ++++- .../server/api/endpoints/notes/timeline.ts | 6 ++++ .../api/endpoints/notes/user-list-timeline.ts | 6 ++++ .../src/server/api/endpoints/users/notes.ts | 8 ++++- packages/backend/src/server/index.ts | 2 ++ packages/client/src/pages/admin/settings.vue | 8 ----- 34 files changed, 180 insertions(+), 99 deletions(-) create mode 100644 packages/backend/migration/1626509500668-fix-remote-file-proxy.js diff --git a/.config/example.yml b/.config/example.yml index 38d5e59ea..4da739935 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -155,6 +155,9 @@ id: 'aid' # Media Proxy #mediaProxy: https://example.com/proxy +# Proxy remote files (default: false) +#proxyRemoteFiles: true + # Sign to ActivityPub GET request (default: false) #signToActivityPubGet: true diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index cdfdfcec9..af88f382e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -325,8 +325,6 @@ disablingTimelinesInfo: "これらのタイムラインを無効化しても、 registration: "登録" enableRegistration: "誰でも新規登録できるようにする" invite: "招待" -proxyRemoteFiles: "リモートのファイルをプロキシする" -proxyRemoteFilesDescription: "この設定を有効にすると、未保存または保存容量超過で削除されたリモートファイルをローカルでプロキシし、サムネイルも生成するようになります。サーバーのストレージには影響しません、" driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量" driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量" inMb: "メガバイト単位" diff --git a/packages/backend/migration/1626509500668-fix-remote-file-proxy.js b/packages/backend/migration/1626509500668-fix-remote-file-proxy.js new file mode 100644 index 000000000..163f73c66 --- /dev/null +++ b/packages/backend/migration/1626509500668-fix-remote-file-proxy.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class fixRemoteFileProxy1626509500668 { + constructor() { + this.name = 'fixRemoteFileProxy1626509500668'; + } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarUrl"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerUrl"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyRemoteFiles"`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "proxyRemoteFiles" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`); + await queryRunner.query(`ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`); + await queryRunner.query(`ALTER TABLE "user" ADD "bannerUrl" character varying(512)`); + await queryRunner.query(`ALTER TABLE "user" ADD "avatarUrl" character varying(512)`); + } +} + +exports.fixRemoteFileProxy1626509500668 = fixRemoteFileProxy1626509500668; diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts index e3ca6c1ab..7a1b83c99 100644 --- a/packages/backend/src/config/types.ts +++ b/packages/backend/src/config/types.ts @@ -62,6 +62,7 @@ export type Source = { }; mediaProxy?: string; + proxyRemoteFiles?: boolean; signToActivityPubGet?: boolean; }; diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index 4328c8fbf..0b1e9c85c 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -137,11 +137,6 @@ export class Meta { }) public cacheRemoteFiles: boolean; - @Column('boolean', { - default: false, - }) - public proxyRemoteFiles: boolean; - @Column({ ...id(), nullable: true, diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 3eecde1f1..9d5db10eb 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -106,26 +106,6 @@ export class User { }) public tags: string[]; - @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; - @Column('boolean', { default: false, comment: 'Whether the User is suspended.', diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/models/repositories/drive-file.ts index 27afe435e..6452632db 100644 --- a/packages/backend/src/models/repositories/drive-file.ts +++ b/packages/backend/src/models/repositories/drive-file.ts @@ -41,7 +41,7 @@ export class DriveFileRepository extends Repository { return file.properties; } - public getPublicUrl(file: DriveFile, thumbnail = false, meta?: Meta): string | null { + public getPublicUrl(file: DriveFile, thumbnail = false): string | null { // リモートかつメディアプロキシ if (file.uri != null && file.userHost != null && config.mediaProxy != null) { return appendQuery(config.mediaProxy, query({ @@ -51,7 +51,7 @@ export class DriveFileRepository extends Repository { } // リモートかつ期限切れはローカルプロキシを試みる - if (file.uri != null && file.isLink && meta && meta.proxyRemoteFiles) { + if (file.uri != null && file.isLink && config.proxyRemoteFiles) { const key = thumbnail ? file.thumbnailAccessKey : file.webpublicAccessKey; if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外 @@ -136,8 +136,8 @@ export class DriveFileRepository extends Repository { isSensitive: file.isSensitive, blurhash: file.blurhash, properties: opts.self ? file.properties : this.getPublicProperties(file), - url: opts.self ? file.url : this.getPublicUrl(file, false, meta), - thumbnailUrl: this.getPublicUrl(file, true, meta), + url: opts.self ? file.url : this.getPublicUrl(file, false), + thumbnailUrl: this.getPublicUrl(file, true), comment: file.comment, folderId: file.folderId, folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { diff --git a/packages/backend/src/models/repositories/note-reaction.ts b/packages/backend/src/models/repositories/note-reaction.ts index e83e753ec..a212b0d3e 100644 --- a/packages/backend/src/models/repositories/note-reaction.ts +++ b/packages/backend/src/models/repositories/note-reaction.ts @@ -23,10 +23,10 @@ export class NoteReactionRepository extends Repository { return { id: reaction.id, createdAt: reaction.createdAt.toISOString(), - user: await Users.pack(reaction.userId, me), + user: await Users.pack(reaction.user ?? reaction.userId, me), type: convertLegacyReaction(reaction.reaction), ...(opts.withNote ? { - note: await Notes.pack(reaction.noteId, me), + note: await Notes.pack(reaction.note ?? reaction.noteId, me), } : {}), }; } diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 478b1ac28..a909ab3ba 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -1,7 +1,7 @@ import { EntityRepository, Repository, In, Not } from 'typeorm'; import Ajv from 'ajv'; import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '../index.js'; +import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js'; import config from '@/config/index.js'; import { Packed } from '@/misc/schema.js'; import { awaitAll, Promiseable } from '@/prelude/await-all.js'; @@ -182,13 +182,18 @@ export class UserRepository extends Repository { } public getAvatarUrl(user: User): string { - if (user.avatarUrl) { - return user.avatarUrl; + // TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング + if (user.avatar) { + return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); } else { - return `${config.url}/identicon/${user.id}`; + return this.getIdenticonUrl(user.id); } } + public getIdenticonUrl(userId: User['id']): string { + return `${config.url}/identicon/${userId}`; + } + public async pack( src: User['id'] | User, me?: { id: User['id'] } | null | undefined, @@ -202,7 +207,18 @@ export class UserRepository extends Repository { includeSecrets: false, }, options); - const user = typeof src === 'object' ? src : await this.findOneOrFail(src); + let user: User; + + if (typeof src === 'object') { + user = src; + if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOne(src.avatarId) ?? null; + if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOne(src.bannerId) ?? null; + } else { + user = await this.findOneOrFail(src, { + relations: ['avatar', 'banner'], + }); + } + const meId = me ? me.id : null; const isMe = meId === user.id; @@ -232,7 +248,7 @@ export class UserRepository extends Repository { username: user.username, host: user.host, avatarUrl: this.getAvatarUrl(user), - avatarBlurhash: user.avatarBlurhash, + avatarBlurhash: user.avatar?.blurhash || null, avatarColor: null, // 後方互換性のため isAdmin: user.isAdmin || falsy, isModerator: user.isModerator || falsy, @@ -256,8 +272,8 @@ export class UserRepository extends Repository { createdAt: user.createdAt.toISOString(), updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, - bannerUrl: user.bannerUrl, - bannerBlurhash: user.bannerBlurhash, + bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null, + bannerBlurhash: user.banner?.blurhash || null, bannerColor: null, // 後方互換性のため isLocked: user.isLocked, isSilenced: user.isSilenced || falsy, diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 1c03bddcc..bfc613523 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -228,26 +228,14 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { set.cacheRemoteFiles = ps.cacheRemoteFiles; } - if (ps.proxyRemoteFiles !== undefined) { - set.proxyRemoteFiles = ps.proxyRemoteFiles; - } - if (ps.emailRequiredForSignup !== undefined) { set.emailRequiredForSignup = ps.emailRequiredForSignup; } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 7b1b20668..f0cb2ba3c 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -65,10 +65,16 @@ export default define(meta, paramDef, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(`note.id IN (${ antennaQuery.getQuery() })`) .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(antennaQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index b57468d04..57a9fa44b 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -55,10 +55,16 @@ export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.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('note.channel', 'channel'); //#endregion diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 0f0eac4c4..2627884ee 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -64,10 +64,16 @@ export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.id IN (${ clipQuery.getQuery() })`) .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(clipQuery.getParameters()); if (user) { diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index c6291211e..7d9bd44d1 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -71,10 +71,16 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); query.andWhere(new Brackets(qb => { qb .where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`) diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index d8dff4f49..85d0a6254 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -175,12 +175,6 @@ export default define(meta, paramDef, async (ps, _user, token) => { 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.avatarUrl = DriveFiles.getPublicUrl(avatar, true); - - if (avatar.blurhash) { - updates.avatarBlurhash = avatar.blurhash; - } } if (ps.bannerId) { @@ -188,12 +182,6 @@ export default define(meta, paramDef, async (ps, _user, token) => { 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.bannerUrl = DriveFiles.getPublicUrl(banner, false); - - if (banner.blurhash) { - updates.bannerBlurhash = banner.blurhash; - } } if (ps.pinnedPageId) { diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index df959cbb7..312b07579 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -93,10 +93,6 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, - proxyRemoteFiles: { - type: 'boolean', - optional: false, nullable: false, - }, emailRequiredForSignup: { type: 'boolean', optional: false, nullable: false, @@ -529,7 +525,6 @@ export default define(meta, paramDef, async (ps, me) => { pinnedPages: instance.pinnedPages, pinnedClipId: instance.pinnedClipId, cacheRemoteFiles: instance.cacheRemoteFiles, - proxyRemoteFiles: instance.proxyRemoteFiles, requireSetup: (await Users.count({ host: null, })) === 0, diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 07862a7bf..96657f8d3 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -37,10 +37,16 @@ export default define(meta, paramDef, async (ps) => { .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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); 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 38f2c12d2..86dde30d6 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -49,10 +49,16 @@ export default define(meta, paramDef, async (ps, user) => { })); })) .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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 913a49931..6308d2369 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -40,10 +40,16 @@ export default define(meta, paramDef, async (ps, user) => { .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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); 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 a5cd5de63..26aaa0919 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -60,10 +60,16 @@ export default define(meta, paramDef, async (ps, user) => { .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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateRepliesQuery(query, user); if (user) generateMutedUserQuery(query, user); 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 96e9b5d0e..9bcb64b65 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -72,10 +72,16 @@ export default define(meta, paramDef, async (ps, user) => { .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()); generateChannelQuery(query, user); 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 5f21f0e23..12fc88b1f 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -65,10 +65,16 @@ export default define(meta, paramDef, async (ps, user) => { ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateChannelQuery(query, user); generateRepliesQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 269eaa932..eafbba322 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -48,10 +48,16 @@ export default define(meta, paramDef, async (ps, user) => { .orWhere(`'{"${user.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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 629cf014b..43e5d1ef6 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -68,6 +68,7 @@ export default define(meta, paramDef, async (ps, user) => { order: { id: -1, }, + relations: ['user', 'user.avatar', 'user.banner', 'note'], }); return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, user))); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 52440f8a6..87c855a5e 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -52,10 +52,16 @@ export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 10b33ea8c..3053eabe3 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -37,10 +37,16 @@ export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); 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 94fb344c0..c6503eb05 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 @@ -46,10 +46,16 @@ export const paramDef = { export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Notes.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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, me); if (me) 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 1295542ef..e77892b15 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -56,10 +56,16 @@ export default define(meta, paramDef, async (ps, me) => { query .andWhere('note.text ILIKE :q', { q: `%${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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, me); if (me) 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 416e507e9..fde66b241 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -64,10 +64,16 @@ export default define(meta, paramDef, async (ps, user) => { if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) .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()); generateChannelQuery(query, user); 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 990c09f07..0829d0e4c 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 @@ -66,10 +66,16 @@ export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.userId IN (${ listQuery.getQuery() })`) .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(listQuery.getParameters()); generateVisibilityQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 1f61773c4..16318d222 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -63,10 +63,16 @@ export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Notes.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('renote.user', 'renoteUser'); + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me, user); diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index a448925b6..80130e884 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -76,6 +76,8 @@ router.get('/avatar/@:acct', async ctx => { usernameLower: username.toLowerCase(), host: host === config.host ? null : host, isSuspended: false, + }, { + relations: ['avatar'], }); if (user) { diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue index 17f7842ab..5cf4d6c88 100644 --- a/packages/client/src/pages/admin/settings.vue +++ b/packages/client/src/pages/admin/settings.vue @@ -76,11 +76,6 @@ - - - - - @@ -185,7 +180,6 @@ export default defineComponent({ enableGlobalTimeline: false, pinnedUsers: '', cacheRemoteFiles: false, - proxyRemoteFiles: false, localDriveCapacityMb: 0, remoteDriveCapacityMb: 0, enableRegistration: false, @@ -214,7 +208,6 @@ export default defineComponent({ this.enableGlobalTimeline = !meta.disableGlobalTimeline; this.pinnedUsers = meta.pinnedUsers.join('\n'); this.cacheRemoteFiles = meta.cacheRemoteFiles; - this.proxyRemoteFiles = meta.proxyRemoteFiles; this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; this.enableRegistration = !meta.disableRegistration; @@ -241,7 +234,6 @@ export default defineComponent({ disableGlobalTimeline: !this.enableGlobalTimeline, pinnedUsers: this.pinnedUsers.split('\n'), cacheRemoteFiles: this.cacheRemoteFiles, - proxyRemoteFiles: this.proxyRemoteFiles, localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10), disableRegistration: !this.enableRegistration,