2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【後編】NotionドキュメントをAIで差分分析!S3自動アップロードでドキュメントサイトを構築

Last updated at Posted at 2025-12-17

Notionで作成した仕様書をAWSで自動公開するシステムを作ってみた(後編)

この記事は Supershipグループ Advent Calendar 2025 の 18日目の記事です。

TL;DR

  • NotionエクスポートをS3に自動公開するWebアプリを開発しました
    • FastAPI + Next.js 15で構築
    • S3 + CloudFrontでドキュメントをホスティング
  • 後編ではS3自動アップロード機能の実装を解説(前編で差分分析ツールを解説)
  • HTML加工(CSS注入・パンくず自動生成)、S3アップロード、CloudFrontキャッシュ無効化など実装のポイントを紹介

はじめに

前編ではNotionからエクスポートしたHTMLの課題と、その解決方法について説明しました。後編では、実際に構築したフロントエンド・バックエンドシステムの全体像と、各処理の詳細について解説します。

システム全体構成

今回構築したシステムは、Next.js(フロントエンド)とFastAPI(バックエンド)で構成されています。

image.png

フロントエンドとバックエンドの連携フロー

ユーザーがドキュメントを公開するまでの流れを、リクエストとレスポンスを交えて説明します。

image.png

ステップ1: バリデーション(検証)

フロントエンドでZIPファイルと対象の案件を選択すると、自動的にバリデーションAPIが呼び出されます。

リクエスト例(フロントエンド側):

const formData = new FormData();
formData.append("file", file);
formData.append("case", caseName);

const response = await fetch("/app/validation", {
  method: "POST",
  body: formData,
});

バックエンドでの処理:

  1. ZIPファイルをメモリ上で展開
  2. 含まれるHTMLファイルからページIDを抽出
  3. S3上の既存HTMLファイルと照合し、一致率を計算
  4. 一致率が60%以上なら成功、未満なら警告を返す

閾値を60%とした理由は特にないですw

この検証により、誤った案件へのアップロードを防止しています。

レスポンス例:

{
  "status": "success",
  "session_id": "project_session_20241215_143052",
  "match_rate": 85.5,
  "pages": [
    {
      "file_path": "API仕様書 abc123.html",
      "page_name": "API仕様書",
      "page_id": "abc123",
      "filename": "API仕様書 abc123.html"
    }
  ]
}

ステップ2: S3アップロード

検証が成功すると、ユーザーは公開するページを選択してアップロードを実行できます。

リクエスト例:

const formData = new FormData();
formData.append("file", file);
formData.append("case", caseName);
formData.append("session_id", sessionId);
formData.append("selected_pages", JSON.stringify(selectedPageIds));

const response = await fetch("/app/s3-upload-docs", {
  method: "POST",
  body: formData,
});

レスポンス例:

{
  "status": "success",
  "message": "Notion docs uploaded to S3 and CloudFront cache invalidated successfully",
  "cloudfront": {
    "status": "completed",
    "distribution_id": "E1234567890",
    "invalidation_id": "I1234567890"
  }
}

HTML加工処理の詳細

バックエンドでは、NotionからエクスポートしたHTMLに対して複数の加工処理を行います。

処理の流れ

image.png

CSS注入の実装

NotionエクスポートのHTMLは、そのままでは見づらいスタイルになっています。カスタムCSSを注入して、表示を改善します。

注入するCSSの主なスタイル:

/* パンくずナビゲーション - ページ上部に固定表示 */
.breadcrumb-nav {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    z-index: 1000 !important;
    background: rgba(255, 255, 255, 0.95) !important;
    backdrop-filter: blur(10px) !important;
}

/* テーブルの横スクロール対策 */
table.simple-table {
  table-layout: fixed !important;
}

table.simple-table th,
table.simple-table td {
  overflow-wrap: anywhere !important;
  word-break: break-word !important;
}

/* 最終更新日のスタイル */
.last-updated-date-h1 {
    display: block !important;
    font-size: 0.875rem !important;
    color: #666 !important;
}

CSS注入の処理:

バックエンドでは、BeautifulSoupを使ってHTMLを解析し、<head>タグ内に<style>要素を追加します。

def inject_css(soup, css_injection_path):
    """カスタムCSSを注入"""
    style_elem = soup.new_tag('style')
    style_elem.string = css_injection_path.read_text(encoding='utf-8')

    head = soup.find('head')
    if head:
        head.append(style_elem)

パンくずリスト自動生成

Notionのページ階層構造から、自動的にパンくずナビゲーションを生成します。

生成されるHTML:

<div class="breadcrumb-nav">
  <a href="../index.html">仕様書TOP</a>
  <span class="breadcrumb-separator">></span>
  <a href="index.html">API仕様書</a>
  <span class="breadcrumb-separator">></span>
  <span>認証API</span>
</div>

パンくず生成のロジック:

  1. 現在のHTMLファイルのパスから階層を取得
  2. 各階層に対応するindex.htmlまたは親HTMLを探索
  3. 相対パスでリンクを生成
  4. 現在のページ名は最後にリンクなしで表示

Notionのエクスポートでは、ページ階層がディレクトリ構造として表現されます。例えば「仕様書TOP > API仕様書 > 認証API」という階層は、以下のようなディレクトリ構造になります:

プライベート、シェア/
├── index.html                    # 仕様書TOP
├── API仕様書 abc123/
│   ├── index.html               # API仕様書
│   └── 認証API def456.html      # 認証API

この構造を解析して、適切な相対パスでリンクを生成しています。

最終更新日の自動追加

各ページのh1タグの下に、アップロード日時を「最終更新日」として自動追加します。これにより、ドキュメントの鮮度が一目でわかるようになります。

def inject_last_updated_date(soup, html_file):
    """最終更新日をHTMLに追加"""
    current_date = datetime.now().strftime("%Y年%m月%d日")

    first_h1 = soup.find('h1')
    if first_h1:
        date_span = soup.new_tag('span')
        date_span['class'] = 'last-updated-date-h1'
        date_span.string = f"最終更新日: {current_date}"
        first_h1.append(date_span)

S3アップロードとバックアップ

加工が完了したHTMLファイルは、AWS CLIを使ってS3にアップロードします。

アップロード処理

ユーザーが選択したページのみをアップロードする場合と、全ファイルを同期する場合の2パターンに対応しています。

選択アップロード:
特定のページだけ更新したい場合に使用します。index.htmlは常にアップロード対象に含まれます。

全ファイル同期:
aws s3 syncコマンドで、ローカルとS3の差分を同期します。--deleteオプションにより、削除されたページもS3から自動的に削除されます。

自動バックアップ

アップロード時に、日付付きのバックアップディレクトリにも同時保存します。これにより、過去のバージョンを保持し、必要に応じて復元できます。

s3://project-s4-docs/
├── index.html           # 現在公開中
├── api-spec.html
├── ...
└── backup/
    ├── 20241201/        # 12月1日時点のバックアップ
    │   ├── index.html
    │   └── ...
    ├── 20241208/        # 12月8日時点のバックアップ
    └── 20241215/        # 本日のバックアップ

CloudFrontキャッシュの自動無効化

S3へのアップロードが完了しても、CloudFrontのキャッシュが残っていると古いコンテンツが配信され続けます。そこで、アップロード完了後に自動的にキャッシュを無効化します。

無効化処理の流れ

  1. ディストリビューション検索: 案件名を含むCloudFrontディストリビューションを自動検索
  2. キャッシュ無効化実行: 全パス(/*)を対象に無効化リクエストを送信
  3. 完了待機: 無効化が完了するまで待機(最大10分)
# 1. 対象のディストリビューションを検索
response = cloudfront.list_distributions()
for distribution in response['DistributionList']['Items']:
    if <ディストリビーション名> in distribution.get('Comment', ''):
        distribution_id = distribution['Id']
        break

# 2. キャッシュ無効化を実行
cloudfront.create_invalidation(
    DistributionId=distribution_id,
    InvalidationBatch={
        'Paths': {
            'Quantity': 1,
            'Items': ['/*']  # 全パスを無効化
        },
        'CallerReference': f'invalidation-{timestamp}'
    }
)

# 3. 完了を待機
while elapsed_time < 600:
    status = cloudfront.get_invalidation(...)
    if status['Invalidation']['Status'] == 'Completed':
        break
    time.sleep(5)

無効化の所要時間

CloudFrontのキャッシュ無効化は通常1〜2分で完了しますが、混雑時は数分かかることもあります。システムでは最大10分まで待機し、完了を確認してからユーザーに通知します。

まとめ

本システムにより、以下のワークフローが自動化されました:

  1. NotionでドキュメントをHTML形式でエクスポート
  2. Webアプリにアップロード(検証→公開)
  3. 自動的にHTML加工・S3アップロード・キャッシュ削除
  4. 即座に最新ドキュメントが閲覧可能に
  5. 誰でも直感的にアップロードできるように

従来は手作業で行っていたHTML加工やアップロード作業が、ボタン1つで完了するようになりました。

本システムの構築を通じて、NotionとAWSを組み合わせたドキュメント管理の可能性を実感しました。同様の課題を抱えている方の参考になれば幸いです。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?