diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-02-04 14:23:38 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2023-02-04 14:23:38 +0900 |
| commit | 9bde9edcf68430cb98bc08ff74f6804bd56ecf47 (patch) | |
| tree | e09781a1f26bae331968902a79253820e3a80e4e /packages/backend/src/server | |
| parent | Merge branch 'develop' (diff) | |
| parent | 13.3.2 (diff) | |
| download | misskey-9bde9edcf68430cb98bc08ff74f6804bd56ecf47.tar.gz misskey-9bde9edcf68430cb98bc08ff74f6804bd56ecf47.tar.bz2 misskey-9bde9edcf68430cb98bc08ff74f6804bd56ecf47.zip | |
Merge branch 'develop'
Diffstat (limited to 'packages/backend/src/server')
6 files changed, 68 insertions, 39 deletions
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 40024270ae..39bc4c1d96 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -137,38 +137,38 @@ export class FileServerService { try { if (file.state === 'remote') { - const convertFile = async () => { - if (file.fileRole === 'thumbnail') { - if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(file.mime)) { - return this.imageProcessingService.convertToWebpStream( - file.path, - 498, - 280 - ); - } else if (file.mime.startsWith('video/')) { - return await this.videoProcessingService.generateVideoThumbnail(file.path); - } + let image: IImageStreamable | null = null; + + if (file.fileRole === 'thumbnail') { + if (isMimeImage(file.mime, 'sharp-convertible-image')) { + reply.header('Cache-Control', 'max-age=31536000, immutable'); + + const url = new URL(`${this.config.mediaProxy}/static.webp`); + url.searchParams.set('url', file.url); + url.searchParams.set('static', '1'); + return await reply.redirect(301, url.toString()); + } else if (file.mime.startsWith('video/')) { + image = await this.videoProcessingService.generateVideoThumbnail(file.path); } + } - if (file.fileRole === 'webpublic') { - if (['image/svg+xml'].includes(file.mime)) { - return this.imageProcessingService.convertToWebpStream( - file.path, - 2048, - 2048, - { ...webpDefault, lossless: true } - ) - } + if (file.fileRole === 'webpublic') { + if (['image/svg+xml'].includes(file.mime)) { + reply.header('Cache-Control', 'max-age=31536000, immutable'); + + const url = new URL(`${this.config.mediaProxy}/svg.webp`); + url.searchParams.set('url', file.url); + return await reply.redirect(301, url.toString()); } + } - return { + if (!image) { + image = { data: fs.createReadStream(file.path), ext: file.ext, type: file.mime, }; - }; - - const image = await convertFile(); + } if ('pipe' in image.data && typeof image.data.pipe === 'function') { // image.dataがstreamなら、stream終了後にcleanup @@ -180,7 +180,6 @@ export class FileServerService { } reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream'); - reply.header('Cache-Control', 'max-age=31536000, immutable'); return image.data; } @@ -217,6 +216,23 @@ export class FileServerService { return; } + if (this.config.externalMediaProxyEnabled) { + // 外部のメディアプロキシが有効なら、そちらにリダイレクト + + reply.header('Cache-Control', 'public, max-age=259200'); // 3 days + + const url = new URL(`${this.config.mediaProxy}/${request.params.url || ''}`); + + for (const [key, value] of Object.entries(request.query)) { + url.searchParams.append(key, value); + } + + return await reply.redirect( + 301, + url.toString(), + ); + } + // Create temp file const file = await this.getStreamAndTypeFromUrl(url); if (file === '404') { @@ -236,7 +252,7 @@ export class FileServerService { const isAnimationConvertibleImage = isMimeImage(file.mime, 'sharp-animation-convertible-image'); let image: IImageStreamable | null = null; - if ('emoji' in request.query && isConvertibleImage) { + if (('emoji' in request.query || 'avatar' in request.query) && isConvertibleImage) { if (!isAnimationConvertibleImage && !('static' in request.query)) { image = { data: fs.createReadStream(file.path), @@ -246,7 +262,7 @@ export class FileServerService { } else { const data = sharp(file.path, { animated: !('static' in request.query) }) .resize({ - height: 128, + height: 'emoji' in request.query ? 128 : 320, withoutEnlargement: true, }) .webp(webpDefault); @@ -370,7 +386,7 @@ export class FileServerService { @bindThis private async getFileFromKey(key: string): Promise< - { state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; } + { state: 'remote'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; url: string; mime: string; ext: string | null; path: string; cleanup: () => void; } | { state: 'stored_internal'; fileRole: 'thumbnail' | 'webpublic' | 'original'; file: DriveFile; mime: string; ext: string | null; path: string; } | '404' | '204' @@ -392,6 +408,7 @@ export class FileServerService { const result = await this.downloadAndDetectTypeFromUrl(file.uri); return { ...result, + url: file.uri, fileRole: isThumbnail ? 'thumbnail' : isWebpublic ? 'webpublic' : 'original', file, } diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index beb3a34ecd..c7a2c99f94 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -106,7 +106,7 @@ export class ServerService { } } - const url = new URL('/proxy/emoji.webp', this.config.url); + const url = new URL(`${this.config.mediaProxy}/emoji.webp`); // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); url.searchParams.set('emoji', '1'); diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 3baf945323..2fa7a09d49 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -181,6 +181,10 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + mediaProxy: { + type: 'string', + optional: false, nullable: false, + }, features: { type: 'object', optional: true, nullable: false, @@ -307,6 +311,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { policies: { ...DEFAULT_POLICIES, ...instance.policies }, + mediaProxy: this.config.mediaProxy, + ...(ps.detail ? { pinnedPages: instance.pinnedPages, pinnedClipId: instance.pinnedClipId, diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index d583dfb936..befaea4664 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,6 +1,6 @@ import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, BlockingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; +import type { UsersRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; import type { IRemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -11,6 +11,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { DI } from '@/di-symbols.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -77,9 +78,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.blockingsRepository) - private blockingsRepository: BlockingsRepository, - @Inject(DI.pollsRepository) private pollsRepository: PollsRepository, @@ -93,6 +91,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private apRendererService: ApRendererService, private globalEventService: GlobalEventService, private createNotificationService: CreateNotificationService, + private userBlockingService: UserBlockingService, ) { super(meta, paramDef, async (ps, me) => { const createdAt = new Date(); @@ -109,11 +108,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // Check blocking if (note.userId !== me.id) { - const block = await this.blockingsRepository.findOneBy({ - blockerId: note.userId, - blockeeId: me.id, - }); - if (block) { + const blocked = await this.userBlockingService.checkBlocked(note.userId, me.id); + if (blocked) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 36bfa78363..8bb4147b43 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -25,6 +25,8 @@ export interface InternalStreamTypes { remoteUserUpdated: { id: User['id']; }; follow: { followerId: User['id']; followeeId: User['id']; }; unfollow: { followerId: User['id']; followeeId: User['id']; }; + blockingCreated: { blockerId: User['id']; blockeeId: User['id']; }; + blockingDeleted: { blockerId: User['id']; blockeeId: User['id']; }; policiesUpdated: Role['policies']; roleCreated: Role; roleDeleted: Role; diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 802b404ce6..57461b7a33 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -33,7 +33,7 @@ export class UrlPreviewService { private wrap(url?: string): string | null { return url != null ? url.match(/^https?:\/\//) - ? `${this.config.url}/proxy/preview.webp?${query({ + ? `${this.config.mediaProxy}/preview.webp?${query({ url, preview: '1', })}` @@ -73,6 +73,14 @@ export class UrlPreviewService { }); this.logger.succ(`Got preview of ${url}: ${summary.title}`); + + if (summary.url && !(summary.url.startsWith('http://') || summary.url.startsWith('https://'))) { + throw new Error('unsupported schema included'); + } + + if (summary.player?.url && !(summary.player.url.startsWith('http://') || summary.player.url.startsWith('https://'))) { + throw new Error('unsupported schema included'); + } summary.icon = this.wrap(summary.icon); summary.thumbnail = this.wrap(summary.thumbnail); |