FessクローラーのURLからURIへの置き換えで対応できないパターンへのフォールバック対応

Fessのクローラーでは、クロール対象のHTMLページからリンクURLを抽出する際に、java.net.URIを使って相対URLを絶対URLに解決しています。しかし、URI.resolve()はRFC準拠の厳密なパースを行うため、スペースなどの特殊文字を含む相対パスを正しく処理できないケースがありました。今回、この問題に対するフォールバック処理をPR #3056で追加しました。

問題

FessXpathTransformerでHTMLのアンカータグなどからURLを抽出する際、URI.resolve()で相対URLを絶対URLに変換しています。通常の相対パスであれば問題なく動作しますが、以下のようなケースではURIがスキームを検出できず、URLの解決に失敗していました。

  • スペースを含む相対パス(例: page 2.html
  • スペースを含む絶対パス(例: /path with space/page.html
  • スペースを含むプロトコル相対URL(例: //cdn.example.com/a b.js
  • スペースを含む親ディレクトリ参照(例: ../page 2.html

実際のWebサイトでは、こうしたURLは珍しくないため、クロール時にリンクが取りこぼされる原因になっていました。

対応内容

FessXpathTransformeraddChildUrlFromTagAttributeメソッドに、既存の絶対URL判定(スキーム検出)で処理できなかった場合のフォールバック処理を追加しました。ベースURIのスキームとオーソリティを利用して、以下の4パターンの相対URLを手動で絶対URLに変換します。

プロトコル相対URL(//で始まる場合)

// 例: //cdn.example.com/a b.js → http://cdn.example.com/a%20b.js
fallbackUrl = scheme + ":" + urlValue;

クエリ・フラグメントのみ(?または#で始まる場合)

// 例: ?q=test → http://example.com/page.html?q=test
fallbackUrl = uri.toString() + urlValue;

ルート絶対パス(/で始まる場合)

// 例: /path with space/page.html → http://example.com/path%20with%20space/page.html
// /../の正規化も行う

相対パス(上記以外)

// 例: ../page 2.html → http://example.com/dir/page%202.html
// ベースURIのパスから親ディレクトリを算出して結合

いずれのパターンでも、/../がルートを超える場合のパス正規化処理を行い、最終的にencodeUrlでパーセントエンコーディングを適用します。

テスト

6つのテストケースを追加して、各パターンの動作を検証しています。

  • page 2.htmlhttp://example.com/dir/page%202.html
  • /path with space/page.htmlhttp://example.com/path%20with%20space/page.html
  • //cdn.example.com/a b.jshttp://cdn.example.com/a%20b.js
  • ../page 2.html(深い階層から) → http://example.com/dir/page%202.html
  • /../page 2.htmlhttp://example.com/page%202.html
  • ../page 2.html(ルートから) → http://example.com/page%202.html

まとめ

今回の修正は、既存のURI.resolve()で処理できるURLには一切影響を与えず、これまで取りこぼしていたスペース等の特殊文字を含む相対URLを正しく解決できるようになりました。実際のWebサイトでは、CMSが生成するURLにスペースが含まれることもあるため、クロールの網羅性向上に寄与する修正です。

fess-crawlerに一太郎ドキュメントのMIMEタイプ検出を追加

fess-crawlerに、ジャストシステムの一太郎ドキュメントのMIMEタイプ検出機能を追加しました。これにより、Fessのクローラーが一太郎ファイルを正しく識別し、コンテンツを抽出できるようになります。

一太郎のファイル形式

一太郎はジャストシステムが開発した日本語ワープロソフトで、日本の官公庁や企業で広く利用されています。一太郎のファイルはOLE2複合ドキュメント形式を使用しており、バージョンによって複数の拡張子が存在します。

対応する拡張子

今回のMIMEタイプ application/x-js-taro に対応する拡張子は以下の通りです。

拡張子説明
.jtd一太郎 基本文書
.jtt一太郎 テンプレート
.jtdc一太郎 基本文書(ZIP圧縮)
.jttc一太郎 テンプレート(ZIP圧縮)
.jfw一太郎7形式 基本文書
.jvw一太郎7形式 テンプレート
.jsw一太郎ver.4形式 基本文書
.jaw一太郎ver.5形式 基本文書
.jtw一太郎ver.5形式 テンプレート
.jbw一太郎ver.6形式 基本文書
.juw一太郎ver.6形式 テンプレート

MIMEタイプの登録

tika-mimetypes.xmlにMIMEタイプ定義を追加しています。マジックバイト(DOC\x00)とglobパターンの両方で一太郎ファイルを識別できるようにしました。一太郎はOLE2複合ドキュメント形式を使用しているため、application/x-tika-msofficeのサブクラスとして登録しています。

<mime-type type="application/x-js-taro">
  <_comment>JustSystems Ichitaro Document</_comment>
  <magic priority="50">
    <match value="DOC\x00" type="string" offset="0"/>
  </magic>
  <glob pattern="*.jtd"/>
  <glob pattern="*.jtt"/>
  <!-- 他の拡張子も同様に登録 -->
  <sub-class-of type="application/x-tika-msoffice"/>
</mime-type>

Extractorの設定

extractor.xmlapplication/x-js-taroTikaExtractorにマッピングし、一太郎ファイルからのコンテンツ抽出を有効にしています。HWP(Hangul Word Processor)など他のOLE2ベースのフォーマットと同じパターンに従った設定です。

テスト

一太郎ver.4、ver.5、ver.6、一太郎7、一太郎2016の各バージョンのテストリソースファイルを用意し、すべての拡張子に対してMIMEタイプが正しく検出されることを確認するテストケースを追加しています。

変更の詳細はPR #141を参照してください。

Recotem v2.0 フルスタックモダナイゼーション

推薦システム構築ツールRecotemを v2.0 へ大幅にリファクタリングしました。バックエンド、フロントエンド、インフラ、CI/CD、ドキュメントまで全レイヤーを刷新し、今後もメンテナンスを続けられる状態にしています。315ファイルの変更、35,697行の追加という大規模な更新です。

リファクタリングの背景

Recotemはirspackをベースにした推薦システムのモデル構築・管理ツールです。しかし、依存ライブラリやフレームワークのバージョンが古くなり、Python 3.9、Django 3.2、Vue 2といった構成ではメンテナンスの継続が難しい状態でした。今後の開発や運用を見据えて、全面的なモダナイゼーションを実施しました。

バックエンドの刷新(Python 3.9→3.12、Django 3.2→5.1)

依存管理の近代化

requirements.txt から pyproject.toml + uv.lock(uv)に移行しました。uvによる高速な依存解決と再現性のある環境構築が可能になります。

主要ライブラリのアップグレード

ライブラリ旧バージョン新バージョン
Python3.93.12
Django3.25.1
djangorestframework3.123.15
celery5.15.4
numpy1.192.1
pandas1.32.2
scikit-learn0.241.6
SQLAlchemy1.42.0
optuna2.104.1
irspack0.1.160.4.0
psycopg2-binarypsycopg[binary] v3

アーキテクチャの改善

  • サービスレイヤーの導入: api/services/ に model、training、tuning、signing、project の各サービスを追加し、ビューからビジネスロジックを分離
  • WebSocketサポート: Django Channels + consumers.py でリアルタイムなジョブステータス通知を実現
  • ViewSetミックスイン: OwnedResourceMixinCreatedByResourceMixin によるオーナーシップベースのアクセス制御
  • APIバージョニング: /api/v1/ のURLプレフィックスを導入
  • 構造化ログ: JSON形式のログ出力とスロットルレート制御
  • マイグレーション整理: 複数のマイグレーションを 0002_schema_upgrade に集約

セキュリティ強化

  • HMAC-SHA256モデル署名: 学習済みモデルファイルに署名を付与し、改ざんを検知
  • 安全でないファイル形式のアップロード禁止: データアップロードから安全でないシリアライゼーション形式を除外し、任意コード実行のリスクを排除
  • ファイル名サニタイズ: Content-Dispositionヘッダーのファイル名をRFC 6266/5987に準拠してサニタイズ
  • resign_models管理コマンド: 既存の未署名モデルファイルにHMAC署名を追加するマイグレーションコマンド

フロントエンドの完全書き換え

フロントエンドはゼロからの書き直しです。

項目旧構成新構成
フレームワークVue 2 + Vuetify + Options APIVue 3.5 + PrimeVue 4 + Composition API
ビルドツールwebpackVite 6
スタイリングTailwind CSS 4
状態管理Pinia + TanStack Query
型システムTypeScript strict mode
APIクライアントopenapi-generator手動 ofetch クライアント
i18n英語・日本語ロケール

ダークモード対応や、ページベースのルーティング、コンポーザブルの活用など、Vue 3のベストプラクティスに沿った構成にしています。

インフラストラクチャの統合

Dockerの刷新

バックエンド用とCelery用で分かれていたDockerfileを、マルチステージビルドの単一Dockerfileに統合しました。非rootコンテナとしてポート8080でリッスンする構成に変更しています。

Docker Composeも5サービス構成(PostgreSQL、Redis、バックエンド、ワーカー、プロキシ)に再編成し、CI用のオーバーレイ(compose.ci.yaml)も用意しました。

nginxプロキシ

非rootのnginxをポート8000で統一的に配置し、SPA・API・WebSocket・Admin画面のリバースプロキシを一つの設定で管理します。WebSocketルートではJWTトークンがログに漏洩しないようサニタイズされたログフォーマットを使用しています。

Helmチャート

Kubernetes向けのHelmチャートにServiceAccount、PodDisruptionBudget、NetworkPolicy、HorizontalPodAutoscalerを追加し、本番運用に耐える構成にしました。

CI/CDの改善

  • GitHub Actions v4/v5へのアクション更新
  • CodeQLによるセキュリティスキャンの追加
  • Dependabotによる依存関係の自動更新
  • マルチアーキテクチャビルド + Trivyによる脆弱性スキャン
  • Node.js 16→20、Python 3.8→3.12のアップグレード

テストカバレッジ

バックエンド

17の新規/更新テストファイルをpytest + pytest-djangoで整備しました。

  • アクセス制御、WebSocketコンシューマー、シリアライザ、スロットル
  • モデルサービス、トレーニングサービス、署名検証
  • プロジェクト名のユニーク制約、ファイルミックスイン、ページネーション

フロントエンド

  • 30以上のユニットテスト(Vitest)
  • 10以上のE2Eテスト(Playwright)
  • コンポーネント、コンポーザブル、レイアウト、ページ、ルーター、ユーティリティのテスト

ドキュメントの整備

  • CLAUDE.mdCONTRIBUTING.mdSECURITY.mdCODE_OF_CONDUCT.md の追加
  • AWS、GCP、Kubernetes、Docker Composeのデプロイメントガイド
  • .env.example ファイルによる環境変数の説明

まとめ

Recotem v2.0では、バックエンドからフロントエンド、インフラ、CI/CDまで全面的にモダナイゼーションしました。Python 3.12 + Django 5.1、Vue 3.5 + Vite 6といった最新スタックへの移行に加えて、サービスレイヤーの導入やセキュリティ強化、テストカバレッジの充実により、今後のメンテナンスや機能追加がしやすい基盤を整えました。

  • PR #18: v2.0: Full-stack modernization