通知機能ガイド
概要
アプリケーション内での重要なイベントをユーザに通知するシステム。
- 通知センター: ヘッダーのベルアイコン
- トースト通知: Sonnerでリアルタイム表示
- DB永続化: PostgreSQL(Prisma)
ファイル構成
apps/web/lib/
├── services/
│ └── notification-service.ts # 通知発行サービス
├── stores/
│ └── notification-store.ts # Zustandストア
└── i18n/
└── notifications.ts # 翻訳
apps/web/app/api/notifications/
├── route.ts # GET/POST
├── [id]/route.ts # PATCH/DELETE
├── read-all/route.ts # 一括既読
└── unread-count/route.ts # 未読数
apps/web/components/notifications/
├── NotificationBell.tsx # ベルアイコン
├── NotificationDropdown.tsx # ドロップダウン
├── NotificationItem.tsx # 個別アイテム
├── NotificationTypeIcon.tsx # タイプ別アイコン
└── NotificationEmptyState.tsx # 空状態
通知タイプ
| タイプ | 用途 | アイコン | 色 |
|---|
| SYSTEM | システム通知 | Settings | 青 |
| SECURITY | セキュリティ | Shield | 赤 |
| ACTION | アクション要求 | Bell | 紫 |
| INFO | 一般情報 | Info | 灰 |
| WARNING | 警告 | AlertTriangle | 黄 |
| ERROR | エラー | AlertCircle | 赤 |
優先度
| 優先度 | 用途 |
|---|
| URGENT | 緊急(即時対応必要) |
| HIGH | 重要なセキュリティイベント |
| NORMAL | 通常の通知 |
| LOW | 情報通知 |
NotificationService API
基本インポート
typescript
1import { NotificationService } from "@/lib/services/notification-service";
特定ユーザへの通知
typescript
1// セキュリティ通知
2await NotificationService.securityNotify(userId, {
3 title: "New login detected",
4 titleJa: "新しいログインを検出しました",
5 message: "You have logged in successfully.",
6 messageJa: "正常にログインしました。",
7});
8
9// システム通知
10await NotificationService.systemNotify(userId, {
11 title: "Settings updated",
12 titleJa: "設定が更新されました",
13 message: "Your preferences have been saved.",
14 messageJa: "設定が保存されました。",
15});
16
17// アクション通知(クリック可能なリンク付き)
18await NotificationService.actionNotify(userId, {
19 title: "Approval required",
20 titleJa: "承認が必要です",
21 message: "A new request is pending your review.",
22 messageJa: "新しいリクエストが承認待ちです。",
23 actionUrl: "/approvals/123",
24 actionLabel: "Review",
25 actionLabelJa: "確認する",
26});
ブロードキャスト通知(特定ロールへ)
typescript
1await NotificationService.broadcast({
2 role: "ADMIN", // 対象ロール
3 type: "SYSTEM",
4 priority: "HIGH",
5 title: "Configuration updated",
6 titleJa: "設定が更新されました",
7 message: "LDAP configuration has been changed.",
8 messageJa: "LDAP設定が変更されました。",
9 source: "LDAP", // オプション: 通知元
10});
カスタム通知
typescript
1await NotificationService.create({
2 userId: "user-id",
3 type: "WARNING",
4 priority: "NORMAL",
5 title: "Session expiring",
6 titleJa: "セッションが期限切れ間近です",
7 message: "Your session will expire in 5 minutes.",
8 messageJa: "セッションは5分後に期限切れになります。",
9 expiresAt: new Date(Date.now() + 5 * 60 * 1000), // オプション: 有効期限
10 metadata: { sessionId: "abc123" }, // オプション: メタデータ
11});
実装パターン
APIルートでの通知発行
typescript
1// app/api/example/route.ts
2import { NotificationService } from "@/lib/services/notification-service";
3
4export async function POST(request: Request) {
5 const session = await auth();
6 if (!session?.user) {
7 return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
8 }
9
10 try {
11 // ビジネスロジック
12 const result = await doSomething();
13
14 // 対象ユーザに通知
15 await NotificationService.securityNotify(session.user.id, {
16 title: "Action completed",
17 titleJa: "アクションが完了しました",
18 message: "Your request has been processed.",
19 messageJa: "リクエストが処理されました。",
20 }).catch((err) => {
21 // 通知失敗はログのみ、メイン処理には影響させない
22 console.error("[Example] Failed to create notification:", err);
23 });
24
25 return NextResponse.json({ success: true });
26 } catch (error) {
27 return NextResponse.json({ error: "Failed" }, { status: 500 });
28 }
29}
管理者への一斉通知
typescript
1// 全管理者に通知
2await NotificationService.broadcast({
3 role: "ADMIN",
4 type: "SECURITY",
5 priority: "HIGH",
6 title: "User account deleted",
7 titleJa: "ユーザーアカウントが削除されました",
8 message: `User "${deletedUser.name}" has been deleted.`,
9 messageJa: `ユーザー「${deletedUser.name}」が削除されました。`,
10 source: "ADMIN",
11 metadata: { userId: deletedUser.id },
12}).catch((err) => {
13 console.error("[Admin] Failed to create notification:", err);
14});
現在の通知トリガー
セキュリティイベント(対象ユーザへ通知)
| イベント | ファイル |
|---|
| ログイン成功(OpenLDAP) | auth.ts |
| ログイン成功(Google) | auth.config.ts |
| 2FA有効化 | app/api/user/two-factor/enable/route.ts |
| 2FA無効化 | app/api/user/two-factor/disable/route.ts |
| パスワード変更 | app/api/auth/change-password/route.ts |
| ロール変更 | app/api/admin/change-role/route.ts |
| アクセスキー作成/変更/削除 | app/api/admin/access-keys/route.ts |
システムイベント(全管理者へ通知)
| イベント | ファイル |
|---|
| ユーザ削除 | app/api/admin/users/[id]/route.ts |
| OpenLDAP設定変更 | app/api/admin/openldap-config/route.ts |
| LDAP移行設定変更 | app/api/admin/ldap-migration/route.ts |
| 旧LDAP設定変更 | app/api/admin/ldap-migration/route.ts |
| モジュール有効/無効 | app/api/admin/modules/route.ts |
注意事項
通知は自動生成されない
新機能を実装する際、通知が必要な場合は開発者が明示的に NotificationService を呼び出す必要があります。
通知失敗はメイン処理に影響させない
typescript
1// ✅ 推奨: catch でログのみ
2await NotificationService.securityNotify(userId, {...}).catch((err) => {
3 console.error("Failed to create notification:", err);
4});
5
6// ❌ 避ける: 通知失敗で処理全体を失敗させる
7await NotificationService.securityNotify(userId, {...}); // エラーがスローされる可能性
多言語対応を忘れない
typescript
1// ✅ 英語・日本語両方を指定
2{
3 title: "New login detected",
4 titleJa: "新しいログインを検出しました",
5 message: "You have logged in successfully.",
6 messageJa: "正常にログインしました。",
7}
8
9// ❌ 英語のみ
10{
11 title: "New login detected",
12 message: "You have logged in successfully.",
13}
チェックリスト
新しい通知を追加する際: