Fix avatar/banner proxy

This commit is contained in:
mei23 2021-07-17 17:15:12 +09:00
parent da20675ada
commit c43a2ce7be
No known key found for this signature in database
GPG key ID: DD8628500D3E4B23
13 changed files with 46 additions and 88 deletions

View file

@ -155,6 +155,9 @@ id: 'aid'
# Media Proxy # Media Proxy
#mediaProxy: https://example.com/proxy #mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
#proxyRemoteFiles: true
# Sign to ActivityPub GET request (default: false) # Sign to ActivityPub GET request (default: false)
#signToActivityPubGet: true #signToActivityPubGet: true

View file

@ -320,8 +320,6 @@ disablingTimelinesInfo: "これらのタイムラインを無効化しても、
registration: "登録" registration: "登録"
enableRegistration: "誰でも新規登録できるようにする" enableRegistration: "誰でも新規登録できるようにする"
invite: "招待" invite: "招待"
proxyRemoteFiles: "リモートのファイルをプロキシする"
proxyRemoteFilesDescription: "この設定を有効にすると、未保存または保存容量超過で削除されたリモートファイルをローカルでプロキシし、サムネイルも生成するようになります。サーバーのストレージには影響しません、"
driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量" driveCapacityPerLocalAccount: "ローカルユーザーひとりあたりのドライブ容量"
driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量" driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのドライブ容量"
inMb: "メガバイト単位" inMb: "メガバイト単位"

View file

@ -0,0 +1,21 @@
import {MigrationInterface, QueryRunner} from "typeorm";
export class fixRemoteFileProxy1626509500668 implements MigrationInterface {
name = 'fixRemoteFileProxy1626509500668'
public async up(queryRunner: QueryRunner): Promise<void> {
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"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
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)`);
}
}

View file

@ -6,11 +6,6 @@
<template #desc>{{ $ts.cacheRemoteFilesDescription }}</template> <template #desc>{{ $ts.cacheRemoteFilesDescription }}</template>
</FormSwitch> </FormSwitch>
<FormSwitch v-model:value="proxyRemoteFiles">
{{ $ts.proxyRemoteFiles }}
<template #desc>{{ $ts.proxyRemoteFilesDescription }}</template>
</FormSwitch>
<FormInput v-model:value="localDriveCapacityMb" type="number"> <FormInput v-model:value="localDriveCapacityMb" type="number">
<span>{{ $ts.driveCapacityPerLocalAccount }}</span> <span>{{ $ts.driveCapacityPerLocalAccount }}</span>
<template #suffix>MB</template> <template #suffix>MB</template>
@ -59,7 +54,6 @@ export default defineComponent({
icon: 'fas fa-cloud' icon: 'fas fa-cloud'
}, },
cacheRemoteFiles: false, cacheRemoteFiles: false,
proxyRemoteFiles: false,
localDriveCapacityMb: 0, localDriveCapacityMb: 0,
remoteDriveCapacityMb: 0, remoteDriveCapacityMb: 0,
} }
@ -73,14 +67,12 @@ export default defineComponent({
async init() { async init() {
const meta = await os.api('meta', { detail: true }); const meta = await os.api('meta', { detail: true });
this.cacheRemoteFiles = meta.cacheRemoteFiles; this.cacheRemoteFiles = meta.cacheRemoteFiles;
this.proxyRemoteFiles = meta.proxyRemoteFiles;
this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb; this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb; this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
}, },
save() { save() {
os.apiWithDialog('admin/update-meta', { os.apiWithDialog('admin/update-meta', {
cacheRemoteFiles: this.cacheRemoteFiles, cacheRemoteFiles: this.cacheRemoteFiles,
proxyRemoteFiles: this.proxyRemoteFiles,
localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10), localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10), remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
}).then(() => { }).then(() => {

View file

@ -60,6 +60,7 @@ export type Source = {
}; };
mediaProxy?: string; mediaProxy?: string;
proxyRemoteFiles?: boolean;
signToActivityPubGet?: boolean; signToActivityPubGet?: boolean;
}; };

View file

@ -131,11 +131,6 @@ export class Meta {
}) })
public cacheRemoteFiles: boolean; public cacheRemoteFiles: boolean;
@Column('boolean', {
default: false,
})
public proxyRemoteFiles: boolean;
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,

View file

@ -106,26 +106,6 @@ export class User {
}) })
public tags: string[]; 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', { @Column('boolean', {
default: false, default: false,
comment: 'Whether the User is suspended.' comment: 'Whether the User is suspended.'

View file

@ -30,7 +30,7 @@ export class DriveFileRepository extends Repository<DriveFile> {
); );
} }
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) { if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
return appendQuery(config.mediaProxy, query({ return appendQuery(config.mediaProxy, query({
@ -40,7 +40,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; const key = thumbnail ? file.thumbnailAccessKey : file.webpublicAccessKey;
if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外 if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
@ -113,8 +113,6 @@ export class DriveFileRepository extends Repository<DriveFile> {
const file = typeof src === 'object' ? src : await this.findOne(src); const file = typeof src === 'object' ? src : await this.findOne(src);
if (file == null) return null; if (file == null) return null;
const meta = await fetchMeta();
return await awaitAll({ return await awaitAll({
id: file.id, id: file.id,
createdAt: file.createdAt.toISOString(), createdAt: file.createdAt.toISOString(),
@ -125,8 +123,8 @@ export class DriveFileRepository extends Repository<DriveFile> {
isSensitive: file.isSensitive, isSensitive: file.isSensitive,
blurhash: file.blurhash, blurhash: file.blurhash,
properties: file.properties, properties: file.properties,
url: opts.self ? file.url : this.getPublicUrl(file, false, meta), url: opts.self ? file.url : this.getPublicUrl(file, false),
thumbnailUrl: this.getPublicUrl(file, true, meta), thumbnailUrl: this.getPublicUrl(file, true),
comment: file.comment, comment: file.comment,
folderId: file.folderId, folderId: file.folderId,
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {

View file

@ -1,7 +1,7 @@
import $ from 'cafy'; import $ from 'cafy';
import { EntityRepository, Repository, In, Not } from 'typeorm'; import { EntityRepository, Repository, In, Not } from 'typeorm';
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user'; import { User, ILocalUser, IRemoteUser } from '@/models/entities/user';
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '../index'; import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index';
import config from '@/config/index'; import config from '@/config/index';
import { SchemaType } from '@/misc/schema'; import { SchemaType } from '@/misc/schema';
import { awaitAll } from '@/prelude/await-all'; import { awaitAll } from '@/prelude/await-all';
@ -170,7 +170,18 @@ export class UserRepository extends Repository<User> {
includeSecrets: false includeSecrets: false
}, options); }, 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 meId = me ? me.id : null;
const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null;
@ -188,8 +199,8 @@ export class UserRepository extends Repository<User> {
name: user.name, name: user.name,
username: user.username, username: user.username,
host: user.host, host: user.host,
avatarUrl: user.avatarUrl ? user.avatarUrl : config.url + '/avatar/' + user.id, avatarUrl: user.avatar ? DriveFiles.getPublicUrl(user.avatar, true) : config.url + '/avatar/' + user.id,
avatarBlurhash: user.avatarBlurhash, avatarBlurhash: user.avatar?.blurhash || null,
avatarColor: null, // 後方互換性のため avatarColor: null, // 後方互換性のため
isAdmin: user.isAdmin || falsy, isAdmin: user.isAdmin || falsy,
isModerator: user.isModerator || falsy, isModerator: user.isModerator || falsy,
@ -212,8 +223,8 @@ export class UserRepository extends Repository<User> {
createdAt: user.createdAt.toISOString(), createdAt: user.createdAt.toISOString(),
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
lastFetchedAt: user.lastFetchedAt?.toISOString(), lastFetchedAt: user.lastFetchedAt?.toISOString(),
bannerUrl: user.bannerUrl, bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null,
bannerBlurhash: user.bannerBlurhash, bannerBlurhash: user.banner?.blurhash || null,
bannerColor: null, // 後方互換性のため bannerColor: null, // 後方互換性のため
isLocked: user.isLocked, isLocked: user.isLocked,
isModerator: user.isModerator || falsy, isModerator: user.isModerator || falsy,

View file

@ -231,26 +231,14 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
const avatarId = avatar ? avatar.id : null; const avatarId = avatar ? avatar.id : null;
const bannerId = banner ? banner.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, { await Users.update(user!.id, {
avatarId, avatarId,
bannerId, bannerId,
avatarUrl,
bannerUrl,
avatarBlurhash,
bannerBlurhash
}); });
user!.avatarId = avatarId; user!.avatarId = avatarId;
user!.bannerId = bannerId; user!.bannerId = bannerId;
user!.avatarUrl = avatarUrl;
user!.bannerUrl = bannerUrl;
user!.avatarBlurhash = avatarBlurhash;
user!.bannerBlurhash = bannerBlurhash;
//#endregion //#endregion
//#region カスタム絵文字取得 //#region カスタム絵文字取得
@ -343,14 +331,10 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
if (avatar) { if (avatar) {
updates.avatarId = avatar.id; updates.avatarId = avatar.id;
updates.avatarUrl = DriveFiles.getPublicUrl(avatar, true);
updates.avatarBlurhash = avatar.blurhash;
} }
if (banner) { if (banner) {
updates.bannerId = banner.id; updates.bannerId = banner.id;
updates.bannerUrl = DriveFiles.getPublicUrl(banner);
updates.bannerBlurhash = banner.blurhash;
} }
// Update user // Update user

View file

@ -89,10 +89,6 @@ export const meta = {
validator: $.optional.bool, validator: $.optional.bool,
}, },
proxyRemoteFiles: {
validator: $.optional.bool,
},
enableHcaptcha: { enableHcaptcha: {
validator: $.optional.bool, validator: $.optional.bool,
}, },
@ -370,10 +366,6 @@ export default define(meta, async (ps, me) => {
set.cacheRemoteFiles = ps.cacheRemoteFiles; set.cacheRemoteFiles = ps.cacheRemoteFiles;
} }
if (ps.proxyRemoteFiles !== undefined) {
set.proxyRemoteFiles = ps.proxyRemoteFiles;
}
if (ps.enableHcaptcha !== undefined) { if (ps.enableHcaptcha !== undefined) {
set.enableHcaptcha = ps.enableHcaptcha; set.enableHcaptcha = ps.enableHcaptcha;
} }

View file

@ -195,12 +195,6 @@ export default define(meta, async (ps, _user, token) => {
if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar);
if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); 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) { if (ps.bannerId) {
@ -208,12 +202,6 @@ export default define(meta, async (ps, _user, token) => {
if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner);
if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); 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) { if (ps.pinnedPageId) {

View file

@ -100,10 +100,6 @@ export const meta = {
type: 'boolean' as const, type: 'boolean' as const,
optional: false as const, nullable: false as const optional: false as const, nullable: false as const
}, },
proxyRemoteFiles: {
type: 'boolean' as const,
optional: false as const, nullable: false as const
},
enableHcaptcha: { enableHcaptcha: {
type: 'boolean' as const, type: 'boolean' as const,
optional: false as const, nullable: false as const optional: false as const, nullable: false as const
@ -522,7 +518,6 @@ export default define(meta, async (ps, me) => {
pinnedPages: instance.pinnedPages, pinnedPages: instance.pinnedPages,
pinnedClipId: instance.pinnedClipId, pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles, cacheRemoteFiles: instance.cacheRemoteFiles,
proxyRemoteFiles: instance.proxyRemoteFiles,
requireSetup: (await Users.count({ requireSetup: (await Users.count({
host: null, host: null,
})) === 0, })) === 0,