Merge remote-tracking branch 'misskey-dev/develop' into io

This commit is contained in:
まっちゃとーにゅ
2024-02-09 03:17:31 +09:00
75 changed files with 1904 additions and 1367 deletions

View File

@@ -81,9 +81,9 @@
"@fastify/view": "8.2.0",
"@misskey-dev/sharp-read-bmp": "^1.1.1",
"@misskey-dev/summaly": "^5.0.3",
"@nestjs/common": "10.3.1",
"@nestjs/core": "10.3.1",
"@nestjs/testing": "10.3.1",
"@nestjs/common": "10.3.2",
"@nestjs/core": "10.3.2",
"@nestjs/testing": "10.3.2",
"@peertube/http-signature": "1.7.0",
"@simplewebauthn/server": "9.0.1",
"@sinonjs/fake-timers": "11.2.2",
@@ -98,9 +98,9 @@
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"body-parser": "1.20.2",
"bullmq": "5.1.5",
"bullmq": "5.1.9",
"cacheable-lookup": "7.0.0",
"cbor": "9.0.1",
"cbor": "9.0.2",
"chalk": "5.3.0",
"chalk-template": "1.1.0",
"chokidar": "3.5.3",
@@ -115,7 +115,7 @@
"file-type": "19.0.0",
"fluent-ffmpeg": "2.1.2",
"form-data": "4.0.0",
"got": "14.1.0",
"got": "14.2.0",
"happy-dom": "10.0.3",
"hpagent": "1.2.0",
"http-link-header": "1.1.1",
@@ -127,7 +127,7 @@
"jsdom": "23.2.0",
"json5": "2.2.3",
"jsonld": "8.3.2",
"jsrsasign": "11.0.0",
"jsrsasign": "11.1.0",
"meilisearch": "0.37.0",
"mfm-js": "0.24.0",
"microformats-parser": "2.0.2",
@@ -135,10 +135,10 @@
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.1",
"nanoid": "5.0.4",
"nanoid": "5.0.5",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"nodemailer": "6.9.8",
"nodemailer": "6.9.9",
"nsfwjs": "2.4.2",
"oauth": "0.10.0",
"oauth2orize": "1.12.0",
@@ -147,7 +147,7 @@
"otpauth": "9.2.2",
"parse5": "7.1.2",
"pg": "8.11.3",
"pino": "8.17.2",
"pino": "8.18.0",
"pino-pretty": "10.3.1",
"pkce-challenge": "4.1.0",
"probe-image-size": "7.2.3",
@@ -186,9 +186,9 @@
"devDependencies": {
"@jest/globals": "29.7.0",
"@misskey-dev/eslint-plugin": "1.0.0",
"@nestjs/platform-express": "10.3.1",
"@nestjs/platform-express": "10.3.2",
"@simplewebauthn/types": "9.0.1",
"@swc/jest": "0.2.31",
"@swc/jest": "0.2.36",
"@types/accepts": "1.3.7",
"@types/archiver": "6.0.2",
"@types/bcryptjs": "2.4.6",
@@ -197,14 +197,14 @@
"@types/content-disposition": "0.5.8",
"@types/fluent-ffmpeg": "2.1.24",
"@types/http-link-header": "1.0.5",
"@types/jest": "29.5.11",
"@types/jest": "29.5.12",
"@types/js-yaml": "4.0.9",
"@types/jsdom": "21.1.6",
"@types/jsonld": "1.5.13",
"@types/jsrsasign": "10.5.12",
"@types/mime-types": "2.1.4",
"@types/ms": "0.7.34",
"@types/node": "20.11.10",
"@types/node": "20.11.16",
"@types/nodemailer": "6.4.14",
"@types/oauth": "0.9.4",
"@types/oauth2orize": "1.11.3",
@@ -216,7 +216,7 @@
"@types/random-seed": "0.3.5",
"@types/ratelimiter": "3.4.6",
"@types/rename": "1.0.7",
"@types/sanitize-html": "2.9.5",
"@types/sanitize-html": "2.11.0",
"@types/semver": "7.5.6",
"@types/simple-oauth2": "5.0.7",
"@types/sinonjs__fake-timers": "8.1.5",

View File

@@ -397,7 +397,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
*/
@bindThis
public checkDuplicate(name: string): Promise<boolean> {
return this.emojisRepository.exist({ where: { name, host: IsNull() } });
return this.emojisRepository.exists({ where: { name, host: IsNull() } });
}
@bindThis

View File

@@ -419,6 +419,10 @@ export class MfmService {
},
text: (node) => {
if (!node.props.text.match(/[\r\n]/)) {
return doc.createTextNode(node.props.text);
}
const el = doc.createElement('span');
const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));

View File

@@ -610,7 +610,7 @@ export class NoteCreateService implements OnApplicationShutdown {
if (data.reply) {
// 通知
if (data.reply.userHost === null) {
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
where: {
userId: data.reply.userId,
threadId: data.reply.threadId ?? data.reply.id,
@@ -778,7 +778,7 @@ export class NoteCreateService implements OnApplicationShutdown {
@bindThis
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
where: {
userId: u.id,
threadId: note.threadId ?? note.id,

View File

@@ -49,7 +49,7 @@ export class NoteReadService implements OnApplicationShutdown {
//#endregion
// スレッドミュート
const isThreadMuted = await this.noteThreadMutingsRepository.exist({
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
where: {
userId: userId,
threadId: note.threadId ?? note.id,
@@ -70,7 +70,7 @@ export class NoteReadService implements OnApplicationShutdown {
// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する
setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => {
const exist = await this.noteUnreadsRepository.exist({ where: { id: unread.id } });
const exist = await this.noteUnreadsRepository.exists({ where: { id: unread.id } });
if (!exist) return;

View File

@@ -74,12 +74,12 @@ export class SignupService {
const secret = generateUserToken();
// Check username duplication
if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
throw new Error('DUPLICATED_USERNAME');
}
// Check deleted username duplication
if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
throw new Error('USED_USERNAME');
}

View File

@@ -143,7 +143,7 @@ export class UserFollowingService implements OnModuleInit {
let autoAccept = false;
// 鍵アカウントであっても、既にフォローされていた場合はスルー
const isFollowing = await this.followingsRepository.exist({
const isFollowing = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: followee.id,
@@ -155,7 +155,7 @@ export class UserFollowingService implements OnModuleInit {
// フォローしているユーザーは自動承認オプション
if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) {
const isFollowed = await this.followingsRepository.exist({
const isFollowed = await this.followingsRepository.exists({
where: {
followerId: followee.id,
followeeId: follower.id,
@@ -169,7 +169,7 @@ export class UserFollowingService implements OnModuleInit {
if (followee.isLocked && !autoAccept) {
autoAccept = !!(await this.accountMoveService.validateAlsoKnownAs(
follower,
(oldSrc, newSrc) => this.followingsRepository.exist({
(oldSrc, newSrc) => this.followingsRepository.exists({
where: {
followeeId: followee.id,
followerId: newSrc.id,
@@ -232,7 +232,7 @@ export class UserFollowingService implements OnModuleInit {
this.cacheService.userFollowingsCache.refresh(follower.id);
const requestExist = await this.followRequestsRepository.exist({
const requestExist = await this.followRequestsRepository.exists({
where: {
followeeId: followee.id,
followerId: follower.id,
@@ -530,7 +530,7 @@ export class UserFollowingService implements OnModuleInit {
}
}
const requestExist = await this.followRequestsRepository.exist({
const requestExist = await this.followRequestsRepository.exists({
where: {
followeeId: followee.id,
followerId: follower.id,

View File

@@ -642,7 +642,7 @@ export class ApInboxService {
return 'skip: follower not found';
}
const isFollowing = await this.followingsRepository.exist({
const isFollowing = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: actor.id,
@@ -699,14 +699,14 @@ export class ApInboxService {
return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません';
}
const requestExist = await this.followRequestsRepository.exist({
const requestExist = await this.followRequestsRepository.exists({
where: {
followerId: actor.id,
followeeId: followee.id,
},
});
const isFollowing = await this.followingsRepository.exist({
const isFollowing = await this.followingsRepository.exists({
where: {
followerId: actor.id,
followeeId: followee.id,

View File

@@ -25,8 +25,21 @@ export class ApMfmService {
}
@bindThis
public getNoteHtml(note: MiNote): string | null {
if (!note.text) return '';
return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
public getNoteHtml(note: MiNote, apAppend?: string) {
let noMisskeyContent = false;
const srcMfm = (note.text ?? '') + (apAppend ?? '');
const parsed = mfm.parse(srcMfm);
if (!apAppend && parsed?.every(n => ['text', 'unicodeEmoji', 'emojiCode', 'mention', 'hashtag', 'url'].includes(n.type))) {
noMisskeyContent = true;
}
const content = this.mfmService.toHtml(parsed, JSON.parse(note.mentionedRemoteUsers));
return {
content,
noMisskeyContent,
};
}
}

View File

@@ -325,7 +325,7 @@ export class ApRendererService {
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
if (inReplyToNote != null) {
const inReplyToUserExist = await this.usersRepository.exist({ where: { id: inReplyToNote.userId } });
const inReplyToUserExist = await this.usersRepository.exists({ where: { id: inReplyToNote.userId } });
if (inReplyToUserExist) {
if (inReplyToNote.uri) {
@@ -389,17 +389,15 @@ export class ApRendererService {
poll = await this.pollsRepository.findOneBy({ noteId: note.id });
}
let apText = text;
let apAppend = '';
if (quote) {
apText += `\n\nRE: ${quote}`;
apAppend += `\n\nRE: ${quote}`;
}
const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
const content = this.apMfmService.getNoteHtml(Object.assign({}, note, {
text: apText,
}));
const { content, noMisskeyContent } = this.apMfmService.getNoteHtml(note, apAppend);
const emojis = await this.getEmojis(note.emojis);
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
@@ -412,9 +410,6 @@ export class ApRendererService {
const asPoll = poll ? {
type: 'Question',
content: this.apMfmService.getNoteHtml(Object.assign({}, note, {
text: text,
})),
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
type: 'Note',
@@ -432,11 +427,13 @@ export class ApRendererService {
attributedTo,
summary: summary ?? undefined,
content: content ?? undefined,
_misskey_content: text,
source: {
content: text,
mediaType: 'text/x.misskeymarkdown',
},
...(noMisskeyContent ? {} : {
_misskey_content: text,
source: {
content: text,
mediaType: 'text/x.misskeymarkdown',
},
}),
_misskey_quote: quote,
quoteUrl: quote,
published: this.idService.parse(note.id).date.toISOString(),

View File

@@ -50,14 +50,14 @@ export class ChannelEntityService {
const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null;
const isFollowing = meId ? await this.channelFollowingsRepository.exist({
const isFollowing = meId ? await this.channelFollowingsRepository.exists({
where: {
followerId: meId,
followeeId: channel.id,
},
}) : false;
const isFavorited = meId ? await this.channelFavoritesRepository.exist({
const isFavorited = meId ? await this.channelFavoritesRepository.exists({
where: {
userId: meId,
channelId: channel.id,

View File

@@ -45,7 +45,7 @@ export class ClipEntityService {
description: clip.description,
isPublic: clip.isPublic,
favoritedCount: await this.clipFavoritesRepository.countBy({ clipId: clip.id }),
isFavorited: meId ? await this.clipFavoritesRepository.exist({ where: { clipId: clip.id, userId: meId } }) : undefined,
isFavorited: meId ? await this.clipFavoritesRepository.exists({ where: { clipId: clip.id, userId: meId } }) : undefined,
});
}

View File

@@ -46,7 +46,7 @@ export class FlashEntityService {
summary: flash.summary,
script: flash.script,
likedCount: flash.likedCount,
isLiked: meId ? await this.flashLikesRepository.exist({ where: { flashId: flash.id, userId: meId } }) : undefined,
isLiked: meId ? await this.flashLikesRepository.exists({ where: { flashId: flash.id, userId: meId } }) : undefined,
});
}

View File

@@ -52,7 +52,7 @@ export class GalleryPostEntityService {
tags: post.tags.length > 0 ? post.tags : undefined,
isSensitive: post.isSensitive,
likedCount: post.likedCount,
isLiked: meId ? await this.galleryLikesRepository.exist({ where: { postId: post.id, userId: meId } }) : undefined,
isLiked: meId ? await this.galleryLikesRepository.exists({ where: { postId: post.id, userId: meId } }) : undefined,
});
}

View File

@@ -108,7 +108,7 @@ export class NoteEntityService implements OnModuleInit {
hide = false;
} else {
// フォロワーかどうか
const isFollowing = await this.followingsRepository.exist({
const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: packedNote.userId,
followerId: meId,

View File

@@ -103,7 +103,7 @@ export class PageEntityService {
eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId, me) : null,
attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is MiDriveFile => x != null), me),
likedCount: page.likedCount,
isLiked: meId ? await this.pageLikesRepository.exist({ where: { pageId: page.id, userId: meId } }) : undefined,
isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined,
});
}

View File

@@ -153,43 +153,43 @@ export class UserEntityService implements OnModuleInit {
followerId: me,
followeeId: target,
}),
this.followingsRepository.exist({
this.followingsRepository.exists({
where: {
followerId: target,
followeeId: me,
},
}),
this.followRequestsRepository.exist({
this.followRequestsRepository.exists({
where: {
followerId: me,
followeeId: target,
},
}),
this.followRequestsRepository.exist({
this.followRequestsRepository.exists({
where: {
followerId: target,
followeeId: me,
},
}),
this.blockingsRepository.exist({
this.blockingsRepository.exists({
where: {
blockerId: me,
blockeeId: target,
},
}),
this.blockingsRepository.exist({
this.blockingsRepository.exists({
where: {
blockerId: target,
blockeeId: me,
},
}),
this.mutingsRepository.exist({
this.mutingsRepository.exists({
where: {
muterId: me,
muteeId: target,
},
}),
this.renoteMutingsRepository.exist({
this.renoteMutingsRepository.exists({
where: {
muterId: me,
muteeId: target,
@@ -216,7 +216,7 @@ export class UserEntityService implements OnModuleInit {
/*
const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId);
const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exist({
const isUnread = (myAntennas.length > 0 ? await this.antennaNotesRepository.exists({
where: {
antennaId: In(myAntennas.map(x => x.id)),
read: false,

View File

@@ -163,12 +163,12 @@ export class SignupApiService {
}
if (instance.emailRequiredForSignup) {
if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
throw new FastifyReplyError(400, 'DUPLICATED_USERNAME');
}
// Check deleted username duplication
if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
throw new FastifyReplyError(400, 'USED_USERNAME');
}

View File

@@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw e;
});
const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } });
const exist = await this.promoNotesRepository.exists({ where: { noteId: note.id } });
if (exist) {
throw new ApiError(meta.errors.alreadyPromoted);

View File

@@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const accessToken = secureRndstr(32);
// Fetch exist access token
const exist = await this.accessTokensRepository.exist({
const exist = await this.accessTokensRepository.exists({
where: {
appId: session.appId,
userId: me.id,

View File

@@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check if already blocking
const exist = await this.blockingsRepository.exist({
const exist = await this.blockingsRepository.exists({
where: {
blockerId: blocker.id,
blockeeId: blockee.id,

View File

@@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check not blocking
const exist = await this.blockingsRepository.exist({
const exist = await this.blockingsRepository.exists({
where: {
blockerId: blocker.id,
blockeeId: blockee.id,

View File

@@ -62,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.noSuchClip);
}
const exist = await this.clipFavoritesRepository.exist({
const exist = await this.clipFavoritesRepository.exists({
where: {
clipId: clip.id,
userId: me.id,

View File

@@ -38,7 +38,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private driveFilesRepository: DriveFilesRepository,
) {
super(meta, paramDef, async (ps, me) => {
const exist = await this.driveFilesRepository.exist({
const exist = await this.driveFilesRepository.exists({
where: {
md5: ps.md5,
userId: me.id,

View File

@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
// if already liked
const exist = await this.flashLikesRepository.exist({
const exist = await this.flashLikesRepository.exists({
where: {
flashId: flash.id,
userId: me.id,

View File

@@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check if already following
const exist = await this.followingsRepository.exist({
const exist = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: followee.id,

View File

@@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check not following
const exist = await this.followingsRepository.exist({
const exist = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: followee.id,

View File

@@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
// if already liked
const exist = await this.galleryLikesRepository.exist({
const exist = await this.galleryLikesRepository.exists({
where: {
postId: post.id,
userId: me.id,

View File

@@ -73,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private downloadService: DownloadService,
) {
super(meta, paramDef, async (ps, me) => {
const userExist = await this.usersRepository.exist({ where: { id: me.id } });
const userExist = await this.usersRepository.exists({ where: { id: me.id } });
if (!userExist) throw new ApiError(meta.errors.noSuchUser);
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (file === null) throw new ApiError(meta.errors.noSuchFile);

View File

@@ -34,7 +34,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
) {
super(meta, paramDef, async (ps, me) => {
if (ps.tokenId) {
const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } });
const tokenExist = await this.accessTokensRepository.exists({ where: { id: ps.tokenId } });
if (tokenExist) {
await this.accessTokensRepository.delete({
@@ -43,7 +43,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
}
} else if (ps.token) {
const tokenExist = await this.accessTokensRepository.exist({ where: { token: ps.token } });
const tokenExist = await this.accessTokensRepository.exists({ where: { token: ps.token } });
if (tokenExist) {
await this.accessTokensRepository.delete({

View File

@@ -85,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// Check if already muting
const exist = await this.mutingsRepository.exist({
const exist = await this.mutingsRepository.exists({
where: {
muterId: muter.id,
muteeId: mutee.id,

View File

@@ -261,7 +261,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Check blocking
if (renote.userId !== me.id) {
const blockExist = await this.blockingsRepository.exist({
const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: renote.userId,
blockeeId: me.id,
@@ -309,7 +309,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Check blocking
if (reply.userId !== me.id) {
const blockExist = await this.blockingsRepository.exist({
const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: reply.userId,
blockeeId: me.id,

View File

@@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// if already favorited
const exist = await this.noteFavoritesRepository.exist({
const exist = await this.noteFavoritesRepository.exists({
where: {
noteId: note.id,
userId: me.id,

View File

@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
// if already liked
const exist = await this.pageLikesRepository.exist({
const exist = await this.pageLikesRepository.exists({
where: {
pageId: page.id,
userId: me.id,

View File

@@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw err;
});
const exist = await this.promoReadsRepository.exist({
const exist = await this.promoReadsRepository.exists({
where: {
noteId: note.id,
userId: me.id,

View File

@@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (me == null) {
throw new ApiError(meta.errors.forbidden);
} else if (me.id !== user.id) {
const isFollowing = await this.followingsRepository.exist({
const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: user.id,
followerId: me.id,

View File

@@ -109,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (me == null) {
throw new ApiError(meta.errors.forbidden);
} else if (me.id !== user.id) {
const isFollowing = await this.followingsRepository.exist({
const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: user.id,
followerId: me.id,

View File

@@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private roleService: RoleService,
) {
super(meta, paramDef, async (ps, me) => {
const listExist = await this.userListsRepository.exist({
const listExist = await this.userListsRepository.exists({
where: {
id: ps.listId,
isPublic: true,
@@ -123,7 +123,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
if (currentUser.id !== me.id) {
const blockExist = await this.blockingsRepository.exist({
const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: currentUser.id,
blockeeId: me.id,
@@ -134,7 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}
const exist = await this.userListMembershipsRepository.exist({
const exist = await this.userListMembershipsRepository.exists({
where: {
userListId: userList.id,
userId: currentUser.id,

View File

@@ -48,7 +48,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
const userListExist = await this.userListsRepository.exist({
const userListExist = await this.userListsRepository.exists({
where: {
id: ps.listId,
isPublic: true,
@@ -59,7 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new ApiError(meta.errors.noSuchList);
}
const exist = await this.userListFavoritesRepository.exist({
const exist = await this.userListFavoritesRepository.exists({
where: {
userId: me.id,
userListId: ps.listId,

View File

@@ -105,7 +105,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Check blocking
if (user.id !== me.id) {
const blockExist = await this.blockingsRepository.exist({
const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: user.id,
blockeeId: me.id,
@@ -116,7 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}
const exist = await this.userListMembershipsRepository.exist({
const exist = await this.userListMembershipsRepository.exists({
where: {
userListId: userList.id,
userId: user.id,

View File

@@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
userListId: ps.listId,
});
if (me !== null) {
additionalProperties.isLiked = await this.userListFavoritesRepository.exist({
additionalProperties.isLiked = await this.userListFavoritesRepository.exists({
where: {
userId: me.id,
userListId: ps.listId,

View File

@@ -46,7 +46,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private userListFavoritesRepository: UserListFavoritesRepository,
) {
super(meta, paramDef, async (ps, me) => {
const userListExist = await this.userListsRepository.exist({
const userListExist = await this.userListsRepository.exists({
where: {
id: ps.listId,
isPublic: true,

View File

@@ -43,7 +43,7 @@ class UserListChannel extends Channel {
this.withRenotes = params.withRenotes ?? true;
// Check existence and owner
const listExist = await this.userListsRepository.exist({
const listExist = await this.userListsRepository.exists({
where: {
id: this.listId,
userId: this.user!.id,

View File

@@ -0,0 +1,44 @@
import * as assert from 'assert';
import { Test } from '@nestjs/testing';
import { CoreModule } from '@/core/CoreModule.js';
import { ApMfmService } from '@/core/activitypub/ApMfmService.js';
import { GlobalModule } from '@/GlobalModule.js';
import { MiNote } from '@/models/Note.js';
describe('ApMfmService', () => {
let apMfmService: ApMfmService;
beforeAll(async () => {
const app = await Test.createTestingModule({
imports: [GlobalModule, CoreModule],
}).compile();
apMfmService = app.get<ApMfmService>(ApMfmService);
});
describe('getNoteHtml', () => {
test('Do not provide _misskey_content for simple text', () => {
const note: MiNote = {
text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
mentionedRemoteUsers: '[]',
} as any;
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
assert.equal(noMisskeyContent, true, 'noMisskeyContent');
assert.equal(content, '<p>テキスト <a href="http://misskey.local/tags/タグ" rel="tag">#タグ</a> <a href="http://misskey.local/@mention" class="u-url mention">@mention</a> 🍊 :emoji: <a href="https://example.com">https://example.com</a></p>', 'content');
});
test('Provide _misskey_content for MFM', () => {
const note: MiNote = {
text: '$[tada foo]',
mentionedRemoteUsers: '[]',
} as any;
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
assert.equal(noMisskeyContent, false, 'noMisskeyContent');
assert.equal(content, '<p><i>foo</i></p>', 'content');
});
});
});

View File

@@ -33,6 +33,12 @@ describe('MfmService', () => {
const output = '<p><span>foo<br>bar<br>baz</span></p>';
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
});
test('Do not generate unnecessary span', () => {
const input = 'foo $[tada bar]';
const output = '<p>foo <i>bar</i></p>';
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
});
});
describe('fromHtml', () => {