summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/PushNotificationService.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/src/core/PushNotificationService.ts')
-rw-r--r--packages/backend/src/core/PushNotificationService.ts101
1 files changed, 101 insertions, 0 deletions
diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts
new file mode 100644
index 0000000000..31d29bed97
--- /dev/null
+++ b/packages/backend/src/core/PushNotificationService.ts
@@ -0,0 +1,101 @@
+import { Inject, Injectable } from '@nestjs/common';
+import push from 'web-push';
+import { DI } from '@/di-symbols.js';
+import { Config } from '@/config.js';
+import type { Packed } from '@/misc/schema';
+import { getNoteSummary } from '@/misc/get-note-summary.js';
+import { SwSubscriptionsRepository } from '@/models/index.js';
+import { MetaService } from './MetaService.js';
+
+// Defined also packages/sw/types.ts#L14-L21
+type pushNotificationsTypes = {
+ 'notification': Packed<'Notification'>;
+ 'unreadMessagingMessage': Packed<'MessagingMessage'>;
+ 'readNotifications': { notificationIds: string[] };
+ 'readAllNotifications': undefined;
+ 'readAllMessagingMessages': undefined;
+ 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string };
+};
+
+// プッシュメッセージサーバーには文字数制限があるため、内容を削減します
+function truncateNotification(notification: Packed<'Notification'>): any {
+ if (notification.note) {
+ return {
+ ...notification,
+ note: {
+ ...notification.note,
+ // textをgetNoteSummaryしたものに置き換える
+ text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note),
+
+ cw: undefined,
+ reply: undefined,
+ renote: undefined,
+ user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる
+ },
+ };
+ }
+
+ return notification;
+}
+
+@Injectable()
+export class PushNotificationService {
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.swSubscriptionsRepository)
+ private swSubscriptionsRepository: SwSubscriptionsRepository,
+
+ private metaService: MetaService,
+ ) {
+ }
+
+ public async pushNotification<T extends keyof pushNotificationsTypes>(userId: string, type: T, body: pushNotificationsTypes[T]) {
+ const meta = await this.metaService.fetch();
+
+ if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return;
+
+ // アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録
+ push.setVapidDetails(this.config.url,
+ meta.swPublicKey,
+ meta.swPrivateKey);
+
+ // Fetch
+ const subscriptions = await this.swSubscriptionsRepository.findBy({
+ userId: userId,
+ });
+
+ for (const subscription of subscriptions) {
+ const pushSubscription = {
+ endpoint: subscription.endpoint,
+ keys: {
+ auth: subscription.auth,
+ p256dh: subscription.publickey,
+ },
+ };
+
+ push.sendNotification(pushSubscription, JSON.stringify({
+ type,
+ body: type === 'notification' ? truncateNotification(body as Packed<'Notification'>) : body,
+ userId,
+ dateTime: (new Date()).getTime(),
+ }), {
+ proxy: this.config.proxy,
+ }).catch((err: any) => {
+ //swLogger.info(err.statusCode);
+ //swLogger.info(err.headers);
+ //swLogger.info(err.body);
+
+ if (err.statusCode === 410) {
+ this.swSubscriptionsRepository.delete({
+ userId: userId,
+ endpoint: subscription.endpoint,
+ auth: subscription.auth,
+ publickey: subscription.publickey,
+ });
+ }
+ });
+ }
+ }
+}