From 60df819c60824611cff24db1428d9b46a56e6b92 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 17 Feb 2023 15:36:36 +0900
Subject: [PATCH 1/6] refactor: fix types

---
 .../backend/src/core/CustomEmojiService.ts    |  2 +-
 .../src/core/entities/EmojiEntityService.ts   | 43 ++++++----
 packages/backend/src/misc/schema.ts           |  5 +-
 packages/backend/src/models/schema/emoji.ts   | 36 +++++++--
 .../backend/src/server/api/ApiCallService.ts  |  4 +-
 .../endpoints/admin/emoji/add-aliases-bulk.ts |  2 +-
 .../server/api/endpoints/admin/emoji/copy.ts  |  2 +-
 .../api/endpoints/admin/emoji/delete-bulk.ts  |  2 +-
 .../api/endpoints/admin/emoji/delete.ts       |  4 +-
 .../api/endpoints/admin/emoji/list-remote.ts  |  2 +-
 .../server/api/endpoints/admin/emoji/list.ts  |  2 +-
 .../admin/emoji/remove-aliases-bulk.ts        |  2 +-
 .../endpoints/admin/emoji/set-aliases-bulk.ts |  2 +-
 .../admin/emoji/set-category-bulk.ts          |  2 +-
 .../api/endpoints/admin/emoji/update.ts       |  6 +-
 .../src/server/api/endpoints/admin/meta.ts    | 81 ++-----------------
 .../src/server/api/endpoints/emojis.ts        |  6 +-
 .../backend/src/server/api/endpoints/meta.ts  |  2 +-
 .../backend/src/server/api/stream/types.ts    |  4 +-
 19 files changed, 91 insertions(+), 118 deletions(-)

diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index 63f0319442..a1a257fbd1 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -61,7 +61,7 @@ export class CustomEmojiService {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
 			this.globalEventService.publishBroadcastStream('emojiAdded', {
-				emoji: await this.emojiEntityService.pack(emoji.id),
+				emoji: await this.emojiEntityService.packDetailed(emoji.id),
 			});
 		}
 
diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts
index f4a01ab032..f5c8f2d4bb 100644
--- a/packages/backend/src/core/entities/EmojiEntityService.ts
+++ b/packages/backend/src/core/entities/EmojiEntityService.ts
@@ -5,44 +5,59 @@ import type { Packed } from '@/misc/schema.js';
 import type { } from '@/models/entities/Blocking.js';
 import type { Emoji } from '@/models/entities/Emoji.js';
 import { bindThis } from '@/decorators.js';
-import { UserEntityService } from './UserEntityService.js';
 
 @Injectable()
 export class EmojiEntityService {
 	constructor(
 		@Inject(DI.emojisRepository)
 		private emojisRepository: EmojisRepository,
-
-		private userEntityService: UserEntityService,
 	) {
 	}
 
 	@bindThis
-	public async pack(
+	public async packSimple(
 		src: Emoji['id'] | Emoji,
-		opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = { omitHost: true, omitId: true, withUrl: true },
-	): Promise<Packed<'Emoji'>> {
-		opts = { omitHost: true, omitId: true, withUrl: true, ...opts };
-
+	): Promise<Packed<'EmojiSimple'>> {
 		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
 
 		return {
-			id: opts.omitId ? undefined : emoji.id,
 			aliases: emoji.aliases,
 			name: emoji.name,
 			category: emoji.category,
-			host: opts.omitHost ? undefined : emoji.host,
 			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
-			url: opts.withUrl ? (emoji.publicUrl || emoji.originalUrl) : undefined,
+			url: emoji.publicUrl || emoji.originalUrl,
 		};
 	}
 
 	@bindThis
-	public packMany(
+	public packSimpleMany(
 		emojis: any[],
-		opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = {},
 	) {
-		return Promise.all(emojis.map(x => this.pack(x, opts)));
+		return Promise.all(emojis.map(x => this.packSimple(x)));
+	}
+
+	@bindThis
+	public async packDetailed(
+		src: Emoji['id'] | Emoji,
+	): Promise<Packed<'EmojiDetailed'>> {
+		const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src });
+
+		return {
+			id: emoji.id,
+			aliases: emoji.aliases,
+			name: emoji.name,
+			category: emoji.category,
+			host: emoji.host,
+			// || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ)
+			url: emoji.publicUrl || emoji.originalUrl,
+		};
+	}
+
+	@bindThis
+	public packDetailedMany(
+		emojis: any[],
+	) {
+		return Promise.all(emojis.map(x => this.packDetailed(x)));
 	}
 }
 
diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts
index 6bd714b0f9..7fc4a3e654 100644
--- a/packages/backend/src/misc/schema.ts
+++ b/packages/backend/src/misc/schema.ts
@@ -26,7 +26,7 @@ import { packedClipSchema } from '@/models/schema/clip.js';
 import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js';
 import { packedQueueCountSchema } from '@/models/schema/queue.js';
 import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js';
-import { packedEmojiSchema } from '@/models/schema/emoji.js';
+import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/schema/emoji.js';
 import { packedFlashSchema } from '@/models/schema/flash.js';
 
 export const refs = {
@@ -57,7 +57,8 @@ export const refs = {
 	Clip: packedClipSchema,
 	FederationInstance: packedFederationInstanceSchema,
 	GalleryPost: packedGalleryPostSchema,
-	Emoji: packedEmojiSchema,
+	EmojiSimple: packedEmojiSimpleSchema,
+	EmojiDetailed: packedEmojiDetailedSchema,
 	Flash: packedFlashSchema,
 };
 
diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts
index 143f25373c..c00c3dac1d 100644
--- a/packages/backend/src/models/schema/emoji.ts
+++ b/packages/backend/src/models/schema/emoji.ts
@@ -1,11 +1,37 @@
-export const packedEmojiSchema = {
+export const packedEmojiSimpleSchema = {
+	type: 'object',
+	properties: {
+		aliases: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'string',
+				optional: false, nullable: false,
+				format: 'id',
+			},
+		},
+		name: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		category: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		url: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+	},
+} as const;
+
+export const packedEmojiDetailedSchema = {
 	type: 'object',
 	properties: {
 		id: {
 			type: 'string',
-			optional: true, nullable: false,
+			optional: false, nullable: false,
 			format: 'id',
-			example: 'xxxxxxxxxx',
 		},
 		aliases: {
 			type: 'array',
@@ -26,12 +52,12 @@ export const packedEmojiSchema = {
 		},
 		host: {
 			type: 'string',
-			optional: true, nullable: true,
+			optional: false, nullable: true,
 			description: 'The local host is represented with `null`.',
 		},
 		url: {
 			type: 'string',
-			optional: true, nullable: false,
+			optional: false, nullable: false,
 		},
 	},
 } as const;
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 8aa570213d..cc27b36966 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -219,8 +219,8 @@ export class ApiCallService implements OnApplicationShutdown {
 
 			const limit = Object.assign({}, ep.meta.limit);
 
-			if (!limit.key) {
-				limit.key = ep.name;
+			if (limit.key == null) {
+				(limit as any).key = ep.name;
 			}
 
 			// TODO: 毎リクエスト計算するのもあれだしキャッシュしたい
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
index c683cd24c1..0cc60e9191 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
@@ -56,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
 			this.globalEventService.publishBroadcastStream('emojiUpdated', {
-				emojis: await this.emojiEntityService.packMany(ps.ids),
+				emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index b4fc7fd6f5..8885a40fd9 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
 			this.globalEventService.publishBroadcastStream('emojiAdded', {
-				emoji: await this.emojiEntityService.pack(copied.id),
+				emoji: await this.emojiEntityService.packDetailed(copied.id),
 			});
 
 			return {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
index 0c337237d3..f298baaedf 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			}
 
 			this.globalEventService.publishBroadcastStream('emojiDeleted', {
-				emojis: await this.emojiEntityService.packMany(emojis),
+				emojis: await this.emojiEntityService.packDetailedMany(emojis),
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
index c51e7fd1a8..a5fbe3f4ea 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
@@ -4,9 +4,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { EmojisRepository } from '@/models/index.js';
 import { DI } from '@/di-symbols.js';
 import { ModerationLogService } from '@/core/ModerationLogService.js';
-import { ApiError } from '../../../error.js';
 import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { ApiError } from '../../../error.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -57,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
 			this.globalEventService.publishBroadcastStream('emojiDeleted', {
-				emojis: [await this.emojiEntityService.pack(emoji)],
+				emojis: [await this.emojiEntityService.packDetailed(emoji)],
 			});
 
 			this.moderationLogService.insertModerationLog(me, 'deleteEmoji', {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
index 8e0ea2e117..df3c28deff 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
@@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				.take(ps.limit)
 				.getMany();
 
-			return this.emojiEntityService.packMany(emojis, { omitHost: false, omitId: false, withUrl: false });
+			return this.emojiEntityService.packDetailedMany(emojis);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
index 1b1931f8e6..814668294f 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
@@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				emojis = await q.take(ps.limit).getMany();
 			}
 
-			return this.emojiEntityService.packMany(emojis, { omitHost: false, omitId: false, withUrl: false });
+			return this.emojiEntityService.packDetailedMany(emojis);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
index 065965f64a..66547024f7 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
@@ -56,7 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 		
 			this.globalEventService.publishBroadcastStream('emojiUpdated', {
-				emojis: await this.emojiEntityService.packMany(ps.ids),
+				emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
index 51c0f329ac..c8992eeb04 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
@@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
 			this.globalEventService.publishBroadcastStream('emojiUpdated', {
-				emojis: await this.emojiEntityService.packMany(ps.ids),
+				emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
index 3329cab7b9..8a538c1003 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
 			this.globalEventService.publishBroadcastStream('emojiUpdated', {
-				emojis: await this.emojiEntityService.packMany(ps.ids),
+				emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
index af610ddf9c..809bf77d6b 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -3,9 +3,9 @@ import { DataSource } from 'typeorm';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { EmojisRepository } from '@/models/index.js';
 import { DI } from '@/di-symbols.js';
-import { ApiError } from '../../../error.js';
 import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { ApiError } from '../../../error.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 
 			await this.db.queryResultCache!.remove(['meta_emojis']);
 
-			const updated = await this.emojiEntityService.pack(emoji.id);
+			const updated = await this.emojiEntityService.packDetailed(emoji.id);
 
 			if (emoji.name === ps.name) {
 				this.globalEventService.publishBroadcastStream('emojiUpdated', {
@@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				});
 			} else {
 				this.globalEventService.publishBroadcastStream('emojiDeleted', {
-					emojis: [await this.emojiEntityService.pack(emoji)],
+					emojis: [await this.emojiEntityService.packDetailed(emoji)],
 				});
 
 				this.globalEventService.publishBroadcastStream('emojiAdded', {
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 2b19104ea7..9eef1b29c5 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -54,86 +54,22 @@ export const meta = {
 			},
 			mascotImageUrl: {
 				type: 'string',
-				optional: false, nullable: false,
+				optional: false, nullable: true,
 				default: '/assets/ai.png',
 			},
 			bannerUrl: {
 				type: 'string',
-				optional: false, nullable: false,
+				optional: false, nullable: true,
 			},
 			errorImageUrl: {
 				type: 'string',
-				optional: false, nullable: false,
+				optional: false, nullable: true,
 				default: 'https://xn--931a.moe/aiart/yubitun.png',
 			},
 			iconUrl: {
 				type: 'string',
 				optional: false, nullable: true,
 			},
-			maxNoteTextLength: {
-				type: 'number',
-				optional: false, nullable: false,
-			},
-			emojis: {
-				type: 'array',
-				optional: false, nullable: false,
-				items: {
-					type: 'object',
-					optional: false, nullable: false,
-					properties: {
-						id: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'id',
-						},
-						aliases: {
-							type: 'array',
-							optional: false, nullable: false,
-							items: {
-								type: 'string',
-								optional: false, nullable: false,
-							},
-						},
-						category: {
-							type: 'string',
-							optional: false, nullable: true,
-						},
-						host: {
-							type: 'string',
-							optional: false, nullable: true,
-						},
-						url: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'url',
-						},
-					},
-				},
-			},
-			ads: {
-				type: 'array',
-				optional: false, nullable: false,
-				items: {
-					type: 'object',
-					optional: false, nullable: false,
-					properties: {
-						place: {
-							type: 'string',
-							optional: false, nullable: false,
-						},
-						url: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'url',
-						},
-						imageUrl: {
-							type: 'string',
-							optional: false, nullable: false,
-							format: 'url',
-						},
-					},
-				},
-			},
 			enableEmail: {
 				type: 'boolean',
 				optional: false, nullable: false,
@@ -146,10 +82,6 @@ export const meta = {
 				type: 'boolean',
 				optional: false, nullable: false,
 			},
-			proxyAccountName: {
-				type: 'string',
-				optional: false, nullable: true,
-			},
 			userStarForReactionFallback: {
 				type: 'boolean',
 				optional: true, nullable: false,
@@ -228,7 +160,7 @@ export const meta = {
 				optional: true, nullable: true,
 			},
 			smtpPort: {
-				type: 'string',
+				type: 'number',
 				optional: true, nullable: true,
 			},
 			smtpUser: {
@@ -299,6 +231,10 @@ export const meta = {
 				type: 'boolean',
 				optional: true, nullable: false,
 			},
+			policies: {
+				type: 'object',
+				optional: false, nullable: false,
+			},
 		},
 	},
 } as const;
@@ -349,7 +285,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				iconUrl: instance.iconUrl,
 				backgroundImageUrl: instance.backgroundImageUrl,
 				logoImageUrl: instance.logoImageUrl,
-				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
 				defaultLightTheme: instance.defaultLightTheme,
 				defaultDarkTheme: instance.defaultDarkTheme,
 				enableEmail: instance.enableEmail,
diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts
index a909dddfd6..325b758358 100644
--- a/packages/backend/src/server/api/endpoints/emojis.ts
+++ b/packages/backend/src/server/api/endpoints/emojis.ts
@@ -82,11 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			});
 
 			return {
-				emojis: await this.emojiEntityService.packMany(emojis, {
-					omitId: true,
-					omitHost: true,
-					withUrl: true,
-				}),
+				emojis: await this.emojiEntityService.packSimpleMany(emojis),
 			};
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index f7463b8ccf..cdb314a873 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -295,7 +295,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				iconUrl: instance.iconUrl,
 				backgroundImageUrl: instance.backgroundImageUrl,
 				logoImageUrl: instance.logoImageUrl,
-				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
+				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
 				defaultLightTheme: instance.defaultLightTheme,
 				defaultDarkTheme: instance.defaultDarkTheme,
 				ads: ads.map(ad => ({
diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts
index bf6ece2864..9287952cb6 100644
--- a/packages/backend/src/server/api/stream/types.ts
+++ b/packages/backend/src/server/api/stream/types.ts
@@ -42,10 +42,10 @@ export interface InternalStreamTypes {
 
 export interface BroadcastTypes {
 	emojiAdded: {
-		emoji: Packed<'Emoji'>;
+		emoji: Packed<'EmojiDetailed'>;
 	};
 	emojiUpdated: {
-		emojis: Packed<'Emoji'>[];
+		emojis: Packed<'EmojiDetailed'>[];
 	};
 	emojiDeleted: {
 		emojis: {

From 0f546b47d1f649476339864e8ab2b2d0c1121449 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 17 Feb 2023 15:39:10 +0900
Subject: [PATCH 2/6] refactor: fix types

---
 packages/backend/src/server/api/endpoints.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 23aa7fab0f..d05005b078 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -1,4 +1,5 @@
 import type { Schema } from '@/misc/schema.js';
+import { RolePolicies } from '@/core/RoleService.js';
 
 import * as ep___admin_meta from './endpoints/admin/meta.js';
 import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
@@ -659,7 +660,7 @@ export interface IEndpointMeta {
 	 */
 	readonly requireAdmin?: boolean;
 
-	readonly requireRolePolicy?: string;
+	readonly requireRolePolicy?: keyof RolePolicies;
 
 	/**
 	 * エンドポイントのリミテーションに関するやつ

From 36170a11f5b889cae3feda6bd4c666aafee98fef Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 18 Feb 2023 05:16:34 +0000
Subject: [PATCH 3/6] refactor(sw): self => globalThis

---
 packages/sw/src/scripts/create-notification.ts |  8 ++++----
 packages/sw/src/scripts/operations.ts          |  4 ++--
 packages/sw/src/sw.ts                          | 14 +++++++-------
 3 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
index c121b30bef..e45c3f504c 100644
--- a/packages/sw/src/scripts/create-notification.ts
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -23,7 +23,7 @@ export async function createNotification<K extends keyof pushNotificationDataMap
 	const n = await composeNotification(data);
 
 	if (n) {
-		return self.registration.showNotification(...n);
+		return globalThis.registration.showNotification(...n);
 	} else {
 		console.error('Could not compose notification', data);
 		return createEmptyNotification();
@@ -239,7 +239,7 @@ export async function createEmptyNotification() {
 		const i18n = await swLang.i18n as I18n<any>;
 		const { t } = i18n;
 
-		await self.registration.showNotification(
+		await globalThis.registration.showNotification(
 			t('_notification.emptyPushNotificationMessage'),
 			{
 				silent: true,
@@ -253,8 +253,8 @@ export async function createEmptyNotification() {
 		setTimeout(async () => {
 			for (const n of
 				[
-					...(await self.registration.getNotifications({ tag: 'user_visible_auto_notification' })),
-					...(await self.registration.getNotifications({ tag: 'read_notification' })),
+					...(await globalThis.registration.getNotifications({ tag: 'user_visible_auto_notification' })),
+					...(await globalThis.registration.getNotifications({ tag: 'read_notification' })),
 				]
 			) {
 				n.close();
diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts
index 4d693223b2..8936a7763a 100644
--- a/packages/sw/src/scripts/operations.ts
+++ b/packages/sw/src/scripts/operations.ts
@@ -51,11 +51,11 @@ export async function openClient(order: swMessageOrderType, url: string, loginId
 		return client;
 	}
 
-	return self.clients.openWindow(getUrlWithLoginId(url, loginId));
+	return globalThis.clients.openWindow(getUrlWithLoginId(url, loginId));
 }
 
 export async function findClient() {
-	const clients = await self.clients.matchAll({
+	const clients = await globalThis.clients.matchAll({
 		type: 'window',
 	});
 	for (const c of clients) {
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
index c392d03232..f4d7685470 100644
--- a/packages/sw/src/sw.ts
+++ b/packages/sw/src/sw.ts
@@ -6,7 +6,7 @@ import * as swos from '@/scripts/operations';
 import { acct as getAcct } from '@/filters/user';
 
 globalThis.addEventListener('install', ev => {
-	//ev.waitUntil(self.skipWaiting());
+	//ev.waitUntil(globalThis.skipWaiting());
 });
 
 globalThis.addEventListener('activate', ev => {
@@ -17,7 +17,7 @@ globalThis.addEventListener('activate', ev => {
 					.filter((v) => v !== swLang.cacheName)
 					.map(name => caches.delete(name)),
 			))
-			.then(() => self.clients.claim()),
+			.then(() => globalThis.clients.claim()),
 	);
 });
 
@@ -40,7 +40,7 @@ globalThis.addEventListener('fetch', ev => {
 
 globalThis.addEventListener('push', ev => {
 	// クライアント取得
-	ev.waitUntil(self.clients.matchAll({
+	ev.waitUntil(globalThis.clients.matchAll({
 		includeUncontrolled: true,
 		type: 'window',
 	}).then(async (clients: readonly WindowClient[]) => {
@@ -58,24 +58,24 @@ globalThis.addEventListener('push', ev => {
 
 				return createNotification(data);
 			case 'readAllNotifications':
-				for (const n of await self.registration.getNotifications()) {
+				for (const n of await globalThis.registration.getNotifications()) {
 					if (n?.data?.type === 'notification') n.close();
 				}
 				break;
 			case 'readAllAntennas':
-				for (const n of await self.registration.getNotifications()) {
+				for (const n of await globalThis.registration.getNotifications()) {
 					if (n?.data?.type === 'unreadAntennaNote') n.close();
 				}
 				break;
 			case 'readNotifications':
-				for (const n of await self.registration.getNotifications()) {
+				for (const n of await globalThis.registration.getNotifications()) {
 					if (data.body.notificationIds.includes(n.data.body.id)) {
 						n.close();
 					}
 				}
 				break;
 			case 'readAntenna':
-				for (const n of await self.registration.getNotifications()) {
+				for (const n of await globalThis.registration.getNotifications()) {
 					if (n?.data?.type === 'unreadAntennaNote' && data.body.antennaId === n.data.body.antenna.id) {
 						n.close();
 					}

From 8c883653c959fff5de4ddc24d8ee64f59230f880 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 18 Feb 2023 17:48:20 +0900
Subject: [PATCH 4/6] =?UTF-8?q?fix/enhance(sw):=20=E3=83=97=E3=83=83?=
 =?UTF-8?q?=E3=82=B7=E3=83=A5=E9=80=9A=E7=9F=A5=20(=E3=83=90=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=82=B0=E3=83=A9=E3=82=A6=E3=83=B3=E3=83=89=E3=81=A7?=
 =?UTF-8?q?=E9=96=8B=E3=81=84=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4=E5=90=88?=
 =?UTF-8?q?=E3=82=82=E9=80=9A=E7=9F=A5,=20=E3=83=AA=E3=82=A2=E3=82=AF?=
 =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E9=80=9A=E7=9F=A5=E3=81=AF=E3=83=8E?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AB=E3=81=A4=E3=81=8D1=E3=81=A4?=
 =?UTF-8?q?=E3=81=AB)=20(#9977)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(sw): クライアントがあってもpush notificationを無視しない
「プッシュ通知を更新しました」の原因になるため

* enhance(sw): リアクション通知は1つのノートにつき1つしか表示しない
Safari対応で、通知tagは能動的に閉じるように

* revert closeNotificationsByTags
---
 .../sw/src/scripts/create-notification.ts     | 26 +++++++++++--------
 packages/sw/src/sw.ts                         |  6 ++---
 2 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
index e45c3f504c..14f96816a3 100644
--- a/packages/sw/src/scripts/create-notification.ts
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -10,6 +10,12 @@ import { getAccountFromId } from '@/scripts/get-account-from-id';
 import { char2fileName } from '@/scripts/twemoji-base';
 import * as url from '@/scripts/url';
 
+const closeNotificationsByTags = async (tags: string[]) => {
+	for (const n of (await Promise.all(tags.map(tag => globalThis.registration.getNotifications({ tag })))).flat()) {
+		n.close();
+	}
+}
+
 const iconUrl = (name: badgeNames) => `/static-assets/tabler-badges/${name}.png`;
 /* How to add a new badge:
  * 1. Find the icon and download png from https://tabler-icons.io/
@@ -161,6 +167,7 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
 						badge = iconUrl('plus');
 					}
 
+					const tag = `reaction:${data.body.note.id}`;
 					return [`${reaction} ${getUserName(data.body.user)}`, {
 						body: data.body.note.text ?? '',
 						icon: data.body.user.avatarUrl,
@@ -176,10 +183,11 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
 				}
 
 				case 'pollEnded':
+					const tag = `poll:${data.body.note.id}`;
 					return [t('_notification.pollEnded'), {
 						body: data.body.note.text || '',
 						badge: iconUrl('chart-arrows'),
-						tag: `poll:${data.body.note.id}`,
+						tag,
 						data,
 					}];
 
@@ -220,11 +228,12 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
 					return null;
 			}
 		case 'unreadAntennaNote':
+			const tag = `antenna:${data.body.antenna.id}`;
 			return [t('_notification.unreadAntennaNote', { name: data.body.antenna.name }), {
 				body: `${getUserName(data.body.note.user)}: ${data.body.note.text ?? ''}`,
 				icon: data.body.note.user.avatarUrl,
 				badge: iconUrl('antenna'),
-				tag: `antenna:${data.body.antenna.id}`,
+				tag,
 				data,
 				renotify: true,
 			}];
@@ -248,16 +257,11 @@ export async function createEmptyNotification() {
 			},
 		);
 
-		res();
-
 		setTimeout(async () => {
-			for (const n of
-				[
-					...(await globalThis.registration.getNotifications({ tag: 'user_visible_auto_notification' })),
-					...(await globalThis.registration.getNotifications({ tag: 'read_notification' })),
-				]
-			) {
-				n.close();
+			try {
+				await closeNotificationsByTags(['user_visible_auto_notification', 'read_notification']);
+			} finally {
+				res();
 			}
 		}, 1000);
 	});
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
index f4d7685470..6f4c487354 100644
--- a/packages/sw/src/sw.ts
+++ b/packages/sw/src/sw.ts
@@ -53,9 +53,6 @@ globalThis.addEventListener('push', ev => {
 				// 1日以上経過している場合は無視
 				if ((new Date()).getTime() - data.dateTime > 1000 * 60 * 60 * 24) break;
 
-				// クライアントがあったらストリームに接続しているということなので通知しない
-				if (clients.length !== 0) break;
-
 				return createNotification(data);
 			case 'readAllNotifications':
 				for (const n of await globalThis.registration.getNotifications()) {
@@ -83,7 +80,8 @@ globalThis.addEventListener('push', ev => {
 				break;
 		}
 
-		return createEmptyNotification();
+		await createEmptyNotification();
+		return;
 	}));
 });
 

From cd5615d3549e7b46d670b863e77e66a32de80da9 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sat, 18 Feb 2023 14:11:45 +0000
Subject: [PATCH 5/6] fix lint

---
 packages/sw/src/scripts/create-notification.ts | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
index 14f96816a3..da92b37d19 100644
--- a/packages/sw/src/scripts/create-notification.ts
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -14,7 +14,7 @@ const closeNotificationsByTags = async (tags: string[]) => {
 	for (const n of (await Promise.all(tags.map(tag => globalThis.registration.getNotifications({ tag })))).flat()) {
 		n.close();
 	}
-}
+};
 
 const iconUrl = (name: badgeNames) => `/static-assets/tabler-badges/${name}.png`;
 /* How to add a new badge:
@@ -183,11 +183,10 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
 				}
 
 				case 'pollEnded':
-					const tag = `poll:${data.body.note.id}`;
 					return [t('_notification.pollEnded'), {
 						body: data.body.note.text || '',
 						badge: iconUrl('chart-arrows'),
-						tag,
+						tag: `poll:${data.body.note.id}`,
 						data,
 					}];
 
@@ -228,12 +227,11 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
 					return null;
 			}
 		case 'unreadAntennaNote':
-			const tag = `antenna:${data.body.antenna.id}`;
 			return [t('_notification.unreadAntennaNote', { name: data.body.antenna.name }), {
 				body: `${getUserName(data.body.note.user)}: ${data.body.note.text ?? ''}`,
 				icon: data.body.note.user.avatarUrl,
 				badge: iconUrl('antenna'),
-				tag,
+				tag: `antenna:${data.body.antenna.id}`,
 				data,
 				renotify: true,
 			}];

From 2aa73fdf6c91f6f1ec01c5d50d046f2e6e970faf Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Sun, 19 Feb 2023 07:27:14 +0100
Subject: [PATCH 6/6] test(backend): restore AP unit tests (#9987)

---
 packages/backend/test/misc/mock-resolver.ts   | 34 ++++++++++++-
 .../test/{tests => unit}/activitypub.ts       | 49 +++++++++++++------
 2 files changed, 66 insertions(+), 17 deletions(-)
 rename packages/backend/test/{tests => unit}/activitypub.ts (56%)

diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts
index 9efed267ea..6b31e68616 100644
--- a/packages/backend/test/misc/mock-resolver.ts
+++ b/packages/backend/test/misc/mock-resolver.ts
@@ -1,5 +1,16 @@
-import Resolver from '../../src/activitypub/resolver.js';
-import { IObject } from '../../src/activitypub/type.js';
+import type { Config } from '@/config.js';
+import type { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
+import type { ApRendererService } from '@/core/activitypub/ApRendererService.js';
+import type { ApRequestService } from '@/core/activitypub/ApRequestService.js';
+import { Resolver } from '@/core/activitypub/ApResolverService.js';
+import type { IObject } from '@/core/activitypub/type.js';
+import type { HttpRequestService } from '@/core/HttpRequestService.js';
+import type { InstanceActorService } from '@/core/InstanceActorService.js';
+import type { LoggerService } from '@/core/LoggerService.js';
+import type { MetaService } from '@/core/MetaService.js';
+import type { UtilityService } from '@/core/UtilityService.js';
+import { bindThis } from '@/decorators.js';
+import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js';
 
 type MockResponse = {
 	type: string;
@@ -8,6 +19,25 @@ type MockResponse = {
 
 export class MockResolver extends Resolver {
 	private _rs = new Map<string, MockResponse>();
+
+	constructor(loggerService: LoggerService) {
+		super(
+			{} as Config,
+			{} as UsersRepository,
+			{} as NotesRepository,
+			{} as PollsRepository,
+			{} as NoteReactionsRepository,
+			{} as UtilityService,
+			{} as InstanceActorService,
+			{} as MetaService,
+			{} as ApRequestService,
+			{} as HttpRequestService,
+			{} as ApRendererService,
+			{} as ApDbResolverService,
+			loggerService,
+		);
+	}
+
 	public async _register(uri: string, content: string | Record<string, any>, type = 'application/activity+json') {
 		this._rs.set(uri, {
 			type,
diff --git a/packages/backend/test/tests/activitypub.ts b/packages/backend/test/unit/activitypub.ts
similarity index 56%
rename from packages/backend/test/tests/activitypub.ts
rename to packages/backend/test/unit/activitypub.ts
index 19fb5d90d7..3d0032507e 100644
--- a/packages/backend/test/tests/activitypub.ts
+++ b/packages/backend/test/unit/activitypub.ts
@@ -2,8 +2,39 @@ process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import rndstr from 'rndstr';
+import { Test } from '@nestjs/testing';
+import { jest } from '@jest/globals';
+
+import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
+import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { CoreModule } from '@/core/CoreModule.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
+import { LoggerService } from '@/core/LoggerService.js';
+import { MockResolver } from '../misc/mock-resolver.js';
 
 describe('ActivityPub', () => {
+	let noteService: ApNoteService;
+	let personService: ApPersonService;
+	let resolver: MockResolver;
+
+	beforeEach(async () => {
+		const app = await Test.createTestingModule({
+			imports: [GlobalModule, CoreModule],
+		}).compile();
+
+		await app.init();
+		app.enableShutdownHooks();
+
+		noteService = app.get<ApNoteService>(ApNoteService);
+		personService = app.get<ApPersonService>(ApPersonService);
+		resolver = new MockResolver(await app.resolve<LoggerService>(LoggerService));
+
+		// Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error
+		const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService);
+		jest.spyOn(federatedInstanceService, 'fetch').mockImplementation(() => new Promise(() => {}));
+	});
+
 	describe('Parse minimum object', () => {
 		const host = 'https://host1.test';
 		const preferredUsername = `${rndstr('A-Z', 4)}${rndstr('a-z', 4)}`;
@@ -28,13 +59,9 @@ describe('ActivityPub', () => {
 		};
 
 		test('Minimum Actor', async () => {
-			const { MockResolver } = await import('../misc/mock-resolver.js');
-			const { createPerson } = await import('../../src/activitypub/models/person.js');
-
-			const resolver = new MockResolver();
 			resolver._register(actor.id, actor);
 
-			const user = await createPerson(actor.id, resolver);
+			const user = await personService.createPerson(actor.id, resolver);
 
 			assert.deepStrictEqual(user.uri, actor.id);
 			assert.deepStrictEqual(user.username, actor.preferredUsername);
@@ -42,14 +69,10 @@ describe('ActivityPub', () => {
 		});
 
 		test('Minimum Note', async () => {
-			const { MockResolver } = await import('../misc/mock-resolver.js');
-			const { createNote } = await import('../../src/activitypub/models/note.js');
-
-			const resolver = new MockResolver();
 			resolver._register(actor.id, actor);
 			resolver._register(post.id, post);
 
-			const note = await createNote(post.id, resolver, true);
+			const note = await noteService.createNote(post.id, resolver, true);
 
 			assert.deepStrictEqual(note?.uri, post.id);
 			assert.deepStrictEqual(note.visibility, 'public');
@@ -75,13 +98,9 @@ describe('ActivityPub', () => {
 		};
 
 		test('Actor', async () => {
-			const { MockResolver } = await import('../misc/mock-resolver.js');
-			const { createPerson } = await import('../../src/activitypub/models/person.js');
-
-			const resolver = new MockResolver();
 			resolver._register(actor.id, actor);
 
-			const user = await createPerson(actor.id, resolver);
+			const user = await personService.createPerson(actor.id, resolver);
 
 			assert.deepStrictEqual(user.name, actor.name.substr(0, 128));
 		});