parent
8e8ee2ac73
commit
c7d7da8fc5
|
@ -73,6 +73,7 @@
|
||||||
- Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
|
- Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
|
||||||
- Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
|
- Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
|
||||||
- Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
|
- Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
|
||||||
|
- Fix: AP Link等は添付ファイル扱いしないようになど (#13754)
|
||||||
|
|
||||||
## 2024.3.1
|
## 2024.3.1
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { bindThis } from '@/decorators.js';
|
||||||
import { checkHttps } from '@/misc/check-https.js';
|
import { checkHttps } from '@/misc/check-https.js';
|
||||||
import { ApResolverService } from '../ApResolverService.js';
|
import { ApResolverService } from '../ApResolverService.js';
|
||||||
import { ApLoggerService } from '../ApLoggerService.js';
|
import { ApLoggerService } from '../ApLoggerService.js';
|
||||||
import type { IObject } from '../type.js';
|
import { isDocument, type IObject } from '../type.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ApImageService {
|
export class ApImageService {
|
||||||
|
@ -39,7 +39,7 @@ export class ApImageService {
|
||||||
* Imageを作成します。
|
* Imageを作成します。
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
|
public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile | null> {
|
||||||
// 投稿者が凍結されていたらスキップ
|
// 投稿者が凍結されていたらスキップ
|
||||||
if (actor.isSuspended) {
|
if (actor.isSuspended) {
|
||||||
throw new Error('actor has been suspended');
|
throw new Error('actor has been suspended');
|
||||||
|
@ -47,16 +47,18 @@ export class ApImageService {
|
||||||
|
|
||||||
const image = await this.apResolverService.createResolver().resolve(value);
|
const image = await this.apResolverService.createResolver().resolve(value);
|
||||||
|
|
||||||
|
if (!isDocument(image)) return null;
|
||||||
|
|
||||||
if (image.url == null) {
|
if (image.url == null) {
|
||||||
throw new Error('invalid image: url not provided');
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof image.url !== 'string') {
|
if (typeof image.url !== 'string') {
|
||||||
throw new Error('invalid image: unexpected type of url: ' + JSON.stringify(image.url, null, 2));
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkHttps(image.url)) {
|
if (!checkHttps(image.url)) {
|
||||||
throw new Error('invalid image: unexpected schema of url: ' + image.url);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(`Creating the Image: ${image.url}`);
|
this.logger.info(`Creating the Image: ${image.url}`);
|
||||||
|
@ -86,12 +88,11 @@ export class ApImageService {
|
||||||
/**
|
/**
|
||||||
* Imageを解決します。
|
* Imageを解決します。
|
||||||
*
|
*
|
||||||
* Misskeyに対象のImageが登録されていればそれを返し、そうでなければ
|
* ImageをリモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
|
public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile | null> {
|
||||||
// TODO
|
// TODO: Misskeyに対象のImageが登録されていればそれを返す
|
||||||
|
|
||||||
// リモートサーバーからフェッチしてきて登録
|
// リモートサーバーからフェッチしてきて登録
|
||||||
return await this.createImage(actor, value);
|
return await this.createImage(actor, value);
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||||
import promiseLimit from 'promise-limit';
|
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { PollsRepository, EmojisRepository } from '@/models/_.js';
|
import type { PollsRepository, EmojisRepository } from '@/models/_.js';
|
||||||
|
@ -209,15 +208,13 @@ export class ApNoteService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添付ファイル
|
// 添付ファイル
|
||||||
// TODO: attachmentは必ずしもImageではない
|
const files: MiDriveFile[] = [];
|
||||||
// TODO: attachmentは必ずしも配列ではない
|
|
||||||
const limit = promiseLimit<MiDriveFile>(2);
|
for (const attach of toArray(note.attachment)) {
|
||||||
const files = (await Promise.all(toArray(note.attachment).map(attach => (
|
attach.sensitive ||= note.sensitive; // Noteがsensitiveなら添付もsensitiveにする
|
||||||
limit(() => this.apImageService.resolveImage(actor, {
|
const file = await this.apImageService.resolveImage(actor, attach);
|
||||||
...attach,
|
if (file) files.push(file);
|
||||||
sensitive: note.sensitive, // Noteがsensitiveなら添付もsensitiveにする
|
}
|
||||||
}))
|
|
||||||
))));
|
|
||||||
|
|
||||||
// リプライ
|
// リプライ
|
||||||
const reply: MiNote | null = note.inReplyTo
|
const reply: MiNote | null = note.inReplyTo
|
||||||
|
|
|
@ -25,6 +25,7 @@ export interface IObject {
|
||||||
endTime?: Date;
|
endTime?: Date;
|
||||||
icon?: any;
|
icon?: any;
|
||||||
image?: any;
|
image?: any;
|
||||||
|
mediaType?: string;
|
||||||
url?: ApObject | string;
|
url?: ApObject | string;
|
||||||
href?: string;
|
href?: string;
|
||||||
tag?: IObject | IObject[];
|
tag?: IObject | IObject[];
|
||||||
|
@ -240,14 +241,14 @@ export interface IKey extends IObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IApDocument extends IObject {
|
export interface IApDocument extends IObject {
|
||||||
type: 'Document';
|
type: 'Audio' | 'Document' | 'Image' | 'Page' | 'Video';
|
||||||
name: string | null;
|
|
||||||
mediaType: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IApImage extends IObject {
|
export const isDocument = (object: IObject): object is IApDocument =>
|
||||||
|
['Audio', 'Document', 'Image', 'Page', 'Video'].includes(getApType(object));
|
||||||
|
|
||||||
|
export interface IApImage extends IApDocument {
|
||||||
type: 'Image';
|
type: 'Image';
|
||||||
name: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICreate extends IActivity {
|
export interface ICreate extends IActivity {
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { GlobalModule } from '@/GlobalModule.js';
|
||||||
import { CoreModule } from '@/core/CoreModule.js';
|
import { CoreModule } from '@/core/CoreModule.js';
|
||||||
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import type { IActor, IApDocument, ICollection, IPost } from '@/core/activitypub/type.js';
|
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
|
||||||
import { MiMeta, MiNote } from '@/models/_.js';
|
import { MiMeta, MiNote } from '@/models/_.js';
|
||||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { DownloadService } from '@/core/DownloadService.js';
|
import { DownloadService } from '@/core/DownloadService.js';
|
||||||
|
@ -295,7 +295,7 @@ describe('ActivityPub', () => {
|
||||||
await createRandomRemoteUser(resolver, personService),
|
await createRandomRemoteUser(resolver, personService),
|
||||||
imageObject,
|
imageObject,
|
||||||
);
|
);
|
||||||
assert.ok(!driveFile.isLink);
|
assert.ok(driveFile && !driveFile.isLink);
|
||||||
|
|
||||||
const sensitiveImageObject: IApDocument = {
|
const sensitiveImageObject: IApDocument = {
|
||||||
type: 'Document',
|
type: 'Document',
|
||||||
|
@ -308,7 +308,7 @@ describe('ActivityPub', () => {
|
||||||
await createRandomRemoteUser(resolver, personService),
|
await createRandomRemoteUser(resolver, personService),
|
||||||
sensitiveImageObject,
|
sensitiveImageObject,
|
||||||
);
|
);
|
||||||
assert.ok(!sensitiveDriveFile.isLink);
|
assert.ok(sensitiveDriveFile && !sensitiveDriveFile.isLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cacheRemoteFiles=false disables caching', async () => {
|
test('cacheRemoteFiles=false disables caching', async () => {
|
||||||
|
@ -324,7 +324,7 @@ describe('ActivityPub', () => {
|
||||||
await createRandomRemoteUser(resolver, personService),
|
await createRandomRemoteUser(resolver, personService),
|
||||||
imageObject,
|
imageObject,
|
||||||
);
|
);
|
||||||
assert.ok(driveFile.isLink);
|
assert.ok(driveFile && driveFile.isLink);
|
||||||
|
|
||||||
const sensitiveImageObject: IApDocument = {
|
const sensitiveImageObject: IApDocument = {
|
||||||
type: 'Document',
|
type: 'Document',
|
||||||
|
@ -337,7 +337,7 @@ describe('ActivityPub', () => {
|
||||||
await createRandomRemoteUser(resolver, personService),
|
await createRandomRemoteUser(resolver, personService),
|
||||||
sensitiveImageObject,
|
sensitiveImageObject,
|
||||||
);
|
);
|
||||||
assert.ok(sensitiveDriveFile.isLink);
|
assert.ok(sensitiveDriveFile && sensitiveDriveFile.isLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cacheRemoteSensitiveFiles=false only affects sensitive files', async () => {
|
test('cacheRemoteSensitiveFiles=false only affects sensitive files', async () => {
|
||||||
|
@ -353,7 +353,7 @@ describe('ActivityPub', () => {
|
||||||
await createRandomRemoteUser(resolver, personService),
|
await createRandomRemoteUser(resolver, personService),
|
||||||
imageObject,
|
imageObject,
|
||||||
);
|
);
|
||||||
assert.ok(!driveFile.isLink);
|
assert.ok(driveFile && !driveFile.isLink);
|
||||||
|
|
||||||
const sensitiveImageObject: IApDocument = {
|
const sensitiveImageObject: IApDocument = {
|
||||||
type: 'Document',
|
type: 'Document',
|
||||||
|
@ -366,7 +366,19 @@ describe('ActivityPub', () => {
|
||||||
await createRandomRemoteUser(resolver, personService),
|
await createRandomRemoteUser(resolver, personService),
|
||||||
sensitiveImageObject,
|
sensitiveImageObject,
|
||||||
);
|
);
|
||||||
assert.ok(sensitiveDriveFile.isLink);
|
assert.ok(sensitiveDriveFile && sensitiveDriveFile.isLink);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Link is not an attachment files', async () => {
|
||||||
|
const linkObject: IObject = {
|
||||||
|
type: 'Link',
|
||||||
|
href: 'https://example.com/',
|
||||||
|
};
|
||||||
|
const driveFile = await imageService.createImage(
|
||||||
|
await createRandomRemoteUser(resolver, personService),
|
||||||
|
linkObject,
|
||||||
|
);
|
||||||
|
assert.strictEqual(driveFile, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue