# マスタードキュメント - メール通知関連編

> **最終更新**: 2026-03-05
> **対象バージョン**: Phase 1 完了時点（本番稼働中）
> **目的**: このドキュメントだけでメール通知機能の全容を把握できること

---

## 目次

1. [システム概要・全体構成](#1-システム概要全体構成)
2. [データモデル](#2-データモデル)
3. [API仕様（バックエンド）](#3-api仕様バックエンド)
4. [Windmill フロー仕様](#4-windmill-フロー仕様)
5. [画面仕様（フロントエンド）](#5-画面仕様フロントエンド)
6. [本番環境の設定値](#6-本番環境の設定値)
7. [設計判断と制約](#7-設計判断と制約)
8. [運用手順](#8-運用手順)
9. [ソースファイル索引](#9-ソースファイル索引)

---

## 1. システム概要・全体構成

### 機能の目的

複数のメールアカウント（Gmail × 2 + Xserver × 6 = 計8アカウント）に届く大量のメールを自動でフィルタリングし、農家にとって重要なメールだけを LINE で通知する。

### システム構成図

```
[メールサーバー群]          [Windmill]                  [KeinaSystem]
  Gmail (2口)     ─IMAP─▶  mail_filter フロー  ──API──▶  Django バックエンド
  Xserver (6口)             ↓                             (DB記録・ルール参照)
                           [Gemini API]                       ↓
                           LLM判定                        フロントエンド
                             ↓                           ・メール履歴画面
                           LINE Messaging API              ・ルール管理画面
                           (重要と判定した場合のみ通知)        ・フィードバックページ
                                                              ↑
                           LINE通知文にフィードバックURL ────────┘
                           を含め、ユーザーがタップして
                           フィードバックを送信
```

### 処理フロー（1メールあたり）

```
1. IMAP 接続 → 前回処理済み UID 以降の新着メールを取得
2. 宛先補正（To ヘッダー）
   └── @keinafarm.com 宛先は xserver1〜xserver6 に正規化（Gmail先行取り込み時の誤表示防止）
3. 送信者ルール確認（GET /api/mail/sender-rule/）
   ├── never_notify → スキップ（記録しない）
   ├── always_notify → LLMスキップ、即 LINE 通知
   └── ルールなし → 4へ
4. 過去フィードバック集計取得（GET /api/mail/sender-context/）
5. Gemini API で重要度判定（LLM）
6. KeinaSystem に記録（POST /api/mail/emails/）
   ├── not_important → 記録のみ、通知なし
   └── important → フィードバックURLを発行、LINE 通知
7. 処理済み最終 UID を Windmill Variable に保存
```

### 10分ごとの定期実行

Windmill スケジュール `0 */10 * * * *` で自動実行。サーバー上の production Windmill で稼働。

---

## 2. データモデル

### 2.1 MailSender（送信者ルール）

**テーブル名**: `mail_mailsender`

| フィールド | 型 | 説明 |
|---|---|---|
| `id` | BigAutoField | PK |
| `email` | EmailField (null可) | アドレス指定ルールの場合 |
| `domain` | CharField(255, null可) | ドメイン指定ルールの場合 |
| `rule` | CharField(20) | `never_notify` / `always_notify` |
| `note` | TextField | メモ（任意）|
| `created_at` | DateTimeField | 作成日時 |
| `updated_at` | DateTimeField | 更新日時 |

**制約**: `email` と `domain` は必ずどちらか一方のみ設定（DB CHECK 制約 `mail_sender_email_or_domain`）

**ルール判定の優先順位**: アドレスルールが先、次にドメインルール

### 2.2 MailEmail（受信メール記録）

**テーブル名**: `mail_mailemail`

| フィールド | 型 | 説明 |
|---|---|---|
| `id` | BigAutoField | PK |
| `account` | CharField(20) | `gmail` / `gmail_service` / `hotmail` / `xserver1`〜`xserver6`（旧データは `xserver`） |
| `message_id` | CharField(500, unique) | メールの Message-ID ヘッダー（重複防止に使用）|
| `sender_email` | EmailField | 送信者メールアドレス |
| `sender_domain` | CharField(255) | 送信者ドメイン |
| `subject` | CharField(500) | 件名 |
| `body_preview` | TextField | 本文冒頭（最大500文字）|
| `received_at` | DateTimeField | 受信日時 |
| `llm_verdict` | CharField(20) | `important` / `not_important` |
| `notified_at` | DateTimeField (null可) | LINE 通知日時（通知済みの場合のみ）|
| `feedback` | CharField(20, null可) | `important` / `not_important` / `never_notify` / `always_notify` |
| `feedback_at` | DateTimeField (null可) | フィードバック日時 |

**ordering**: `-received_at`（新しい順）

**重複防止**: `message_id` の unique 制約。同じメールが複数アカウントで受信された場合は 2件目以降を「重複メール、スキップ」として処理（400エラーを無視）。

### 2.3 MailNotificationToken（フィードバック用トークン）

**テーブル名**: `mail_mailnotificationtoken`

| フィールド | 型 | 説明 |
|---|---|---|
| `id` | BigAutoField | PK |
| `email` | OneToOneField → MailEmail | |
| `token` | UUIDField (unique) | フィードバック URL 用 UUID |
| `created_at` | DateTimeField | |

**用途**: `important` と判定されたメールに対して作成。`/mail/feedback/<token>/` の URL をLINE通知文に含める。有効期限なし。

---

## 3. API仕様（バックエンド）

ベース URL: `https://main.keinafarm.net/api/mail/`

### 3.1 認証方式

| 認証方式 | 対象エンドポイント | ヘッダー |
|---|---|---|
| APIキー認証（Windmill用） | sender-rule, sender-context, POST emails/ | `X-API-Key: <MAIL_API_KEY>` |
| JWT認証（フロントエンド用） | GET emails/, stats/, senders/, PATCH emails/<pk>/feedback/ | `Authorization: Bearer <token>` |
| 認証不要 | GET/POST feedback/<token>/ | なし |

**MAIL_API_KEY**: `.env.production` の `MAIL_API_KEY` と一致している必要がある。Windmill Variable `u/admin/KEINASYSTEM_API_KEY` に設定。

### 3.2 Windmill向けエンドポイント

#### GET /api/mail/sender-rule/

送信者ルールを確認する。

**リクエスト**: クエリパラメータ `email` `domain`

**レスポンス例**:
```json
{"matched": true, "rule": "never_notify", "match_type": "address"}
{"matched": false}
```

**判定順序**: アドレス一致 → ドメイン一致 → マッチなし

---

#### GET /api/mail/sender-context/

過去フィードバックの集計を返す（LLMへのコンテキスト用）。

**リクエスト**: クエリパラメータ `email` `domain`

**レスポンス例**:
```json
{
  "total_notified": 8,
  "important": 2,
  "not_important": 5,
  "never_notify": 0,
  "no_feedback": 1
}
```

---

#### POST /api/mail/emails/

メールを記録し、`important` の場合はフィードバックURLを発行する。

**リクエストボディ**:
```json
{
  "account": "gmail",
  "message_id": "<unique-message-id>",
  "sender_email": "sender@example.com",
  "sender_domain": "example.com",
  "subject": "件名",
  "body_preview": "本文冒頭...",
  "received_at": "2026-02-25T15:46:00+09:00",
  "llm_verdict": "important"
}
```

**レスポンス例**:
```json
{"id": 69, "feedback_url": "https://main.keinafarm.net/mail/feedback/<uuid>"}
```
`not_important` の場合: `{"id": 68}`（feedback_url なし）

**重複処理**: `message_id` が既存の場合 400 を返す。Windmill 側で「重複メール、スキップ」として処理。

---

### 3.3 フィードバックエンドポイント（認証不要）

#### GET /api/mail/feedback/\<token\>/

フィードバックページ表示用にメール情報を返す。

**レスポンス例**:
```json
{
  "id": 69,
  "sender_email": "sender@example.com",
  "sender_domain": "example.com",
  "subject": "件名",
  "body_preview": "本文...",
  "received_at": "2026-02-25T15:46:00+09:00",
  "feedback": null
}
```

---

#### POST /api/mail/feedback/\<token\>/

フィードバックを保存する。

**リクエストボディ**:
```json
{
  "feedback": "never_notify",
  "scope": "address"
}
```

`feedback` は `important` / `not_important` / `never_notify` / `always_notify` のいずれか。
`scope` は `never_notify` / `always_notify` の場合のみ必要（`address` / `domain`）。

`never_notify` / `always_notify` + scope の場合、`MailSender` レコードを自動 upsert。

---

### 3.4 フロントエンド向けエンドポイント（JWT認証）

#### GET /api/mail/emails/

メール処理履歴を返す（最新100件）。

**クエリパラメータ**: `account`（アカウント絞り込み）、`verdict`（LLM判定絞り込み）

---

#### PATCH /api/mail/emails/\<pk\>/feedback/

履歴画面から直接フィードバックを更新する。

**リクエストボディ**: `feedback`（必須）、`scope`（`never_notify`/`always_notify` 時のみ）

---

#### GET /api/mail/stats/

ダッシュボード用統計。

**レスポンス例**:
```json
{
  "today_processed": 12,
  "today_notified": 3,
  "feedback_pending": 1,
  "total_rules": 5
}
```

---

#### GET /api/mail/senders/

送信者ルール一覧。

#### POST /api/mail/senders/

送信者ルール追加。`email` または `domain` のどちらか一方を指定。

#### DELETE /api/mail/senders/\<id\>/

送信者ルール削除。

---

## 4. Windmill フロー仕様

### 4.1 基本情報

| 項目 | 値 |
|---|---|
| フローパス | `f/mail/mail_filter` |
| スクリプト言語 | Python 3 |
| スケジュール | `0 */10 * * * *`（10分ごと）|
| スケジュールパス | `f/mail/mail_filter_schedule` |
| Windmill URL | `https://windmill.keinafarm.net` |
| ワークスペース | `admins` |

### 4.2 処理対象アカウント

| 変数名 | メールアドレス | サーバー |
|---|---|---|
| `XSERVER1` | `akira@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER2` | `service@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER3` | `midori@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER4` | `kouseiren@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER5` | `post@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER6` | `sales@keinafarm.com` | `sv579.xserver.jp:993` |
| `GMAIL` | `akiracraftwork@gmail.com` | `imap.gmail.com:993`、All Mail |
| `GMAIL2` | `akiranoushi@gmail.com` | `imap.gmail.com:993`、All Mail |

Hotmail は定義済みだがコメントアウト（未有効化）。

### 4.3 Windmill Variables 一覧

本番 Windmill (`windmill.keinafarm.net`、ワークスペース `admins`) に設定。

| Variable パス | 内容 | Secret |
|---|---|---|
| `u/admin/GMAIL_IMAP_USER` | Gmail ユーザー | ✓ |
| `u/admin/GMAIL_IMAP_PASSWORD` | Gmail アプリパスワード | ✓ |
| `u/admin/GMAIL2_IMAP_USER` | Gmail2 ユーザー | ✓ |
| `u/admin/GMAIL2_IMAP_PASSWORD` | Gmail2 アプリパスワード | ✓ |
| `u/admin/XSERVER1_IMAP_USER` | `akira@keinafarm.com` | — |
| `u/admin/XSERVER1_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER2_IMAP_USER` | `service@keinafarm.com` | — |
| `u/admin/XSERVER2_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER3_IMAP_USER` | `midori@keinafarm.com` | — |
| `u/admin/XSERVER3_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER4_IMAP_USER` | `kouseiren@keinafarm.com` | — |
| `u/admin/XSERVER4_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER5_IMAP_USER` | `post@keinafarm.com` | — |
| `u/admin/XSERVER5_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER6_IMAP_USER` | `sales@keinafarm.com` | — |
| `u/admin/XSERVER6_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/GEMINI_API_KEY` | Gemini API キー | ✓ |
| `u/admin/LINE_CHANNEL_ACCESS_TOKEN` | LINE Messaging API トークン | ✓ |
| `u/admin/LINE_TO` | LINE 通知先ユーザー ID | ✓ |
| `u/admin/KEINASYSTEM_API_KEY` | KeinaSystem API キー（`.env.production` の `MAIL_API_KEY` と同値）| ✓ |
| `u/admin/KEINASYSTEM_API_URL` | `https://main.keinafarm.net` | — |
| `u/admin/MAIL_FILTER_GMAIL_LAST_UID` | Gmail 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_GMAIL2_LAST_UID` | Gmail2 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER1_LAST_UID` | Xserver1 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER2_LAST_UID` | Xserver2 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER3_LAST_UID` | Xserver3 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER4_LAST_UID` | Xserver4 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER5_LAST_UID` | Xserver5 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER6_LAST_UID` | Xserver6 最終処理済み UID | — |

### 4.4 LAST_UID の仕組み

- **初回実行**: `LAST_UID` が `0` または未設定の場合、現在の最大 UID を記録して終了（既存メールを遡らない）
- **通常実行**: `LAST_UID + 1` 以降の UID を検索して処理
- **エラー時**: 個別メッセージの処理に失敗しても、成功した最大 UID まで更新する

### 4.5 LLM 判定（Gemini）

モデル: `gemini-2.0-flash`（`temperature=0`, `maxOutputTokens=10`）

プロンプトに渡す情報:
- 送信者アドレス・件名・本文冒頭
- 過去の同一送信者のフィードバック集計（`sender-context` API から取得）

回答: `1`（重要）/ `2`（重要でない）の1文字。`1` で始まる場合 `important`。


### 4.7 宛先補正ロジック

- 対象: Gmail 側で先に取得された転送メール
- 方法: `To` ヘッダーの宛先アドレスを `recipient_map` で `xserver1`〜`xserver6` に変換
- 目的: message_id 重複時に Gmail で先着しても、実際の受信メールボックス（Xserver側）を通知文・履歴で保持する
### 4.6 LINE 通知文フォーマット

```
📧 重要なメールが届きました

宛先: Gmail (メイン)
差出人: sender@example.com
件名: 件名テキスト

フィードバック:
https://main.keinafarm.net/mail/feedback/<uuid>
```

---

## 5. 画面仕様（フロントエンド）

### 5.1 フィードバックページ（認証不要）

**URL**: `/mail/feedback/[token]`
**ファイル**: `frontend/src/app/mail/feedback/[token]/page.tsx`

LINE 通知のリンクから直接アクセス。JWT 認証不要のため、`api` インスタンス（JWT自動付与）ではなく素の `fetch` を使用。

**表示内容**:
- 送信者アドレス・件名・受信日時・本文冒頭
- フィードバックボタン: 重要だった / 普通のメール / 今後通知しない / 常に通知してほしい
- `今後通知しない` / `常に通知` 選択時: アドレス / ドメイン の適用範囲を選択

**状態**:
- 既にフィードバック済みの場合は現在の値をハイライト表示（再選択可能）
- 送信完了後「受け付けました」画面に切り替わる

---

### 5.2 メール処理履歴（JWT認証）

**URL**: `/mail/history`
**Navbar**: 「メール履歴」（History アイコン）
**ファイル**: `frontend/src/app/mail/history/page.tsx`

**表示内容**: 処理したメール最新100件

| カラム | 内容 |
|---|---|
| 受信日時 | 日時 + アカウント名 |
| 送信者 | メールアドレス |
| 件名 | テキスト（truncate）|
| LLM判定 | 重要（赤）/ 通常（灰）バッジ |
| フィードバック | 状態バッジ + 編集ボタン |

**フィルター**: アカウント別・LLM判定別セレクトボックス

**フィードバックモーダル**: 履歴画面からも4択でフィードバックを設定・変更できる。`never_notify` / `always_notify` 選択時はアドレス/ドメインの適用範囲を表示。

---

### 5.3 メール通知ルール管理（JWT認証）

**URL**: `/mail/rules`
**Navbar**: 「メールルール」（Shield アイコン）
**ファイル**: `frontend/src/app/mail/rules/page.tsx`

**追加フォーム**:
- 種別（アドレス / ドメイン）
- ルール（通知しない / 常に通知）
- 値（メールアドレスまたはドメイン名）
- メモ（任意）

**一覧表示**: 種別バッジ・ルールバッジ・値・メモ・設定日・削除ボタン

**ルールが自動追加される場面**: フィードバックで `never_notify` / `always_notify` を選択してスコープを指定すると自動登録される。

---

## 6. 本番環境の設定値

### バックエンド（`.env.production`）

```
MAIL_API_KEY=<Windmill の KEINASYSTEM_API_KEY と同じ値>
```

`settings.py` での参照:
```python
MAIL_API_KEY = os.environ.get('MAIL_API_KEY', '')
FRONTEND_URL = os.environ.get('FRONTEND_URL', 'http://localhost:3000')
```

`FRONTEND_URL` はフィードバック URL の生成に使用（本番値: `https://main.keinafarm.net`）。

### デプロイコマンド

```bash
# サーバー上で実行（--env-file を必ず指定）
cd /home/keinasystem/keinasystem_t02
git pull
docker compose -f docker-compose.prod.yml --env-file .env.production build
docker compose -f docker-compose.prod.yml --env-file .env.production up -d
```

**注意**: `--env-file .env.production` を省略すると SECRET_KEY 等が空になりバックエンドが起動しない。

### Windmill フローの更新手順

ローカルの `flows/mail_filter.flow.json` を編集後:

```bash
cd C:/Users/akira/Develop/windmill_workflow
# サーバーに転送
scp flows/mail_filter.flow.json keinafarm-claude:/tmp/mail_filter.flow.json

# サーバー上でデプロイ（Windmill API 経由）
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; WM=http://172.18.0.15:8000/api/w/admins
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$WM/flows/delete/f/mail/mail_filter"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d @/tmp/mail_filter.flow.json "$WM/flows/create"'
```

---

## 7. 設計判断と制約

### Windmill と KeinaSystem の疎結合

Windmill はスケジューラ・LLM呼び出し担当、KeinaSystem は DB・UI 担当として明確に分離。両者は REST API（APIキー認証）でのみ連携。

→ Windmill の障害が KeinaSystem のメイン機能（作付け計画等）に影響しない。

### APIキー認証の実装

`MailAPIKeyPermission`（`BasePermission` サブクラス）で `X-API-Key` ヘッダーを検証。`secrets.compare_digest` でタイミング攻撃を防止。

`authentication_classes = []` を明示的に設定したビューとしていないビューがある:
- `SenderRuleView`, `SenderContextView`: `authentication_classes = []` を設定済み → 403
- `MailEmailView` の POST: 設定なし（デフォルトの JWTAuthentication が動く）→ キー不一致時は 401

→ **統一するなら** `MailEmailView` にも `authentication_classes = []` を追加すべき（現状は動作上問題なし）。

### フィードバック URL のセキュリティ

UUID v4 のランダムトークンのみで認証。有効期限なし。LINE に送信された URL を知っている人なら誰でもフィードバックを送れる（悪用リスクは低いため許容）。

### 重複メール処理

同じメールが複数アカウントで受信される場合（転送設定等）、`message_id` の unique 制約で2件目以降を自動スキップ。先着レコードを採用するが、Gmail先行時でも `To` ヘッダー宛先補正により `xserver1`〜`xserver6` を優先して記録する。

---

## 8. 運用手順

### 新しいメールアカウントを追加する場合

1. `flows/mail_filter.flow.json` の `ACCOUNTS` リストにエントリを追加
2. 本番 Windmill に Variables を追加（USER, PASSWORD, LAST_UID）
3. フローを再デプロイ

```bash
# Variable 追加例（サーバー上で実行）
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; WM=http://172.18.0.15:8000/api/w/admins
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d "{\"path\":\"u/admin/XSERVER7_IMAP_USER\",\"value\":\"newuser@keinafarm.com\",\"is_secret\":false,\"description\":\"\"}" \
  "$WM/variables/create"'
```

### LINE トークンが期限切れになった場合

LINE Developers Console でトークンを再発行し、本番 Windmill の Variable を更新:

```bash
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; WM=http://172.18.0.15:8000/api/w/admins
# 古いものを削除してから再作成
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$WM/variables/delete/u/admin/LINE_CHANNEL_ACCESS_TOKEN"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d "{\"path\":\"u/admin/LINE_CHANNEL_ACCESS_TOKEN\",\"value\":\"<新トークン>\",\"is_secret\":true,\"description\":\"LINE Messaging APIトークン\"}" \
  "$WM/variables/create"'
```

### フローのログを確認する

```bash
# 最近のジョブ一覧
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh
curl -s -H "Authorization: Bearer $TOKEN" "http://172.18.0.15:8000/api/w/admins/jobs/completed/list?per_page=10" \
  | grep -o "\"id\":\"[^\"]*\"\|\"started_at\":\"[^\"]*\"\|\"script_path\":\"[^\"]*\"" \
  | paste - - - | grep mail_filter'

# ステップジョブのログ取得
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; JOB_ID=<ステップジョブID>
curl -s -H "Authorization: Bearer $TOKEN" \
  "http://172.18.0.15:8000/api/w/admins/jobs/completed/get/$JOB_ID" \
  | grep -o "\"logs\":\"[^\"]*\"" | sed "s/\\\\n/\n/g"'
```

---

## 9. ソースファイル索引

### バックエンド

| ファイル | 内容 |
|---|---|
| `backend/apps/mail/models.py` | MailSender, MailEmail, MailNotificationToken |
| `backend/apps/mail/serializers.py` | MailSenderSerializer, MailEmailCreateSerializer, MailEmailListSerializer, FeedbackDetailSerializer |
| `backend/apps/mail/views.py` | 全ビュー（SenderRuleView, SenderContextView, MailEmailView, MailStatsView, FeedbackView, MailEmailFeedbackView, MailSenderViewSet）+ MailAPIKeyPermission |
| `backend/apps/mail/urls.py` | URL ルーティング |
| `backend/apps/mail/admin.py` | Django 管理画面登録 |
| `backend/apps/mail/migrations/` | マイグレーション |
| `backend/keinasystem/settings.py` | `MAIL_API_KEY`, `FRONTEND_URL` 設定（L161-162）|
| `backend/keinasystem/urls.py` | `path('api/mail/', include('apps.mail.urls'))` |

### フロントエンド

| ファイル | 内容 |
|---|---|
| `frontend/src/app/mail/feedback/[token]/page.tsx` | フィードバックページ（認証不要）|
| `frontend/src/app/mail/history/page.tsx` | メール処理履歴画面 |
| `frontend/src/app/mail/rules/page.tsx` | 送信者ルール管理画面 |
| `frontend/src/components/Navbar.tsx` | 「メール履歴」「メールルール」リンク追加済み |

### Windmill フロー

| ファイル | 内容 |
|---|---|
| `C:/Users/akira/Develop/windmill_workflow/flows/mail_filter.flow.json` | フロー定義（Python スクリプト本体を含む）|

本番 Windmill でのパス: `f/mail/mail_filter`
スケジュール: `f/mail/mail_filter_schedule`



