Skip to content

Commit

Permalink
Fix avatar/banner proxy (#8346)
Browse files Browse the repository at this point in the history
* Fix avatar/banner proxy

Co-authored-by: mei23 <m@m544.net>

* use getAvatarUrl

* fix

* join avatar and banner to improve performance

* join

* Update hybrid-timeline.ts

* fix

Co-authored-by: mei23 <m@m544.net>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
  • Loading branch information
3 people authored Feb 27, 2022
1 parent d071d18 commit e314be5
Show file tree
Hide file tree
Showing 34 changed files with 180 additions and 99 deletions.
3 changes: 3 additions & 0 deletions .config/example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 0 additions & 2 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,6 @@ disablingTimelinesInfo: "これらのタイムラインを無効化しても、
registration: "登録"
enableRegistration: "誰でも新規登録できるようにする"
invite: "招待"
proxyRemoteFiles: "リモートのファイルをプロキシする"
proxyRemoteFilesDescription: "この設定を有効にすると、未保存または保存容量超過で削除されたリモートファイルをローカルでプロキシし、サムネイルも生成するようになります。サーバーのストレージには影響しません、"
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
inMb: "メガバイト単位"
Expand Down
23 changes: 23 additions & 0 deletions packages/backend/migration/1626509500668-fix-remote-file-proxy.js
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions packages/backend/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export type Source = {
};

mediaProxy?: string;
proxyRemoteFiles?: boolean;

signToActivityPubGet?: boolean;
};
Expand Down
5 changes: 0 additions & 5 deletions packages/backend/src/models/entities/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,6 @@ export class Meta {
})
public cacheRemoteFiles: boolean;

@Column('boolean', {
default: false,
})
public proxyRemoteFiles: boolean;

@Column({
...id(),
nullable: true,
Expand Down
20 changes: 0 additions & 20 deletions packages/backend/src/models/entities/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
Expand Down
8 changes: 4 additions & 4 deletions packages/backend/src/models/repositories/drive-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
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({
Expand All @@ -51,7 +51,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
}

// リモートかつ期限切れはローカルプロキシを試みる
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('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
Expand Down Expand Up @@ -136,8 +136,8 @@ export class DriveFileRepository extends Repository<DriveFile> {
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, {
Expand Down
4 changes: 2 additions & 2 deletions packages/backend/src/models/repositories/note-reaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export class NoteReactionRepository extends Repository<NoteReaction> {
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),
} : {}),
};
}
Expand Down
32 changes: 24 additions & 8 deletions packages/backend/src/models/repositories/user.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -182,13 +182,18 @@ export class UserRepository extends Repository<User> {
}

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<ExpectsMe extends boolean | null = null, D extends boolean = false>(
src: User['id'] | User,
me?: { id: User['id'] } | null | undefined,
Expand All @@ -202,7 +207,18 @@ export class UserRepository extends Repository<User> {
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;

Expand Down Expand Up @@ -232,7 +248,7 @@ export class UserRepository extends Repository<User> {
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,
Expand All @@ -256,8 +272,8 @@ export class UserRepository extends Repository<User> {
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,
Expand Down
16 changes: 0 additions & 16 deletions packages/backend/src/remote/activitypub/models/person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,26 +228,14 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us

const avatarId = avatar ? avatar.id : null;
const bannerId = banner ? banner.id : null;
const avatarUrl = avatar ? DriveFiles.getPublicUrl(avatar, true) : null;
const bannerUrl = banner ? DriveFiles.getPublicUrl(banner) : null;
const avatarBlurhash = avatar ? avatar.blurhash : null;
const bannerBlurhash = banner ? banner.blurhash : null;

await Users.update(user!.id, {
avatarId,
bannerId,
avatarUrl,
bannerUrl,
avatarBlurhash,
bannerBlurhash,
});

user!.avatarId = avatarId;
user!.bannerId = bannerId;
user!.avatarUrl = avatarUrl;
user!.bannerUrl = bannerUrl;
user!.avatarBlurhash = avatarBlurhash;
user!.bannerBlurhash = bannerBlurhash;
//#endregion

//#region カスタム絵文字取得
Expand Down Expand Up @@ -340,14 +328,10 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint

if (avatar) {
updates.avatarId = avatar.id;
updates.avatarUrl = DriveFiles.getPublicUrl(avatar, true);
updates.avatarBlurhash = avatar.blurhash;
}

if (banner) {
updates.bannerId = banner.id;
updates.bannerUrl = DriveFiles.getPublicUrl(banner);
updates.bannerBlurhash = banner.blurhash;
}

// Update user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export const paramDef = {
localDriveCapacityMb: { type: 'integer' },
remoteDriveCapacityMb: { type: 'integer' },
cacheRemoteFiles: { type: 'boolean' },
proxyRemoteFiles: { type: 'boolean' },
emailRequiredForSignup: { type: 'boolean' },
enableHcaptcha: { type: 'boolean' },
hcaptchaSiteKey: { type: 'string', nullable: true },
Expand Down Expand Up @@ -175,10 +174,6 @@ export default define(meta, paramDef, async (ps, me) => {
set.cacheRemoteFiles = ps.cacheRemoteFiles;
}

if (ps.proxyRemoteFiles !== undefined) {
set.proxyRemoteFiles = ps.proxyRemoteFiles;
}

if (ps.emailRequiredForSignup !== undefined) {
set.emailRequiredForSignup = ps.emailRequiredForSignup;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/backend/src/server/api/endpoints/antennas/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions packages/backend/src/server/api/endpoints/clips/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 7 additions & 1 deletion packages/backend/src/server/api/endpoints/i/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() })`)
Expand Down
12 changes: 0 additions & 12 deletions packages/backend/src/server/api/endpoints/i/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,25 +175,13 @@ 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) {
const banner = await DriveFiles.findOne(ps.bannerId);

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) {
Expand Down
5 changes: 0 additions & 5 deletions packages/backend/src/server/api/endpoints/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 7 additions & 1 deletion packages/backend/src/server/api/endpoints/notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Loading

0 comments on commit e314be5

Please sign in to comment.