add: URL ストック機能(IndexedDB プロトタイプ) #117 - #123
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 本 PR は、旅程ページに URL をストックする機能を導入するものです。プロトタイプ段階として、データ永続化をサーバー側ではなくフロントエンドの IndexedDB に倒すことで、マイグレーションコストを抑えた設計としています。また、Gemini を利用したテキストの AI 整形機能もあわせて実装されており、ユーザーのメモ作成を効率化します。 Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a "URL Stock" feature that allows users to save URLs to a trip, fetch metadata like titles and thumbnails, and use Gemini AI to format pasted text into markdown notes. The implementation uses IndexedDB (Dexie) for local data persistence during the MVP phase and includes a debug caching utility for local development. Review feedback highlighted a critical SSRF vulnerability in the URL fetching logic and suggested several performance optimizations, such as reusing HTTP and AI client instances. Additionally, improvements were recommended for type safety in database operations and more granular error handling for the AI service integration.
| # --------------------------------------------------------------------------- | ||
|
|
||
|
|
||
| async def _fetch_html(url: str) -> str | None: |
| const id = await db.tripUrls.add({ | ||
| tripId, | ||
| url: validated.url, | ||
| title: validated.title ?? null, | ||
| thumbnailUrl: validated.thumbnailUrl ?? null, | ||
| memo: validated.memo ?? null, | ||
| createdAt: now, | ||
| updatedAt: now, | ||
| } as TripUrlEntry); |
There was a problem hiding this comment.
Dexie の add メソッドに渡すオブジェクトに id が含まれていないため、TripUrlEntry へのキャストが型安全ではありません。TripUrlEntry の id をオプショナルにするか、Omit<TripUrlEntry, 'id'> を使用してキャストを避けるべきです。
| const id = await db.tripUrls.add({ | |
| tripId, | |
| url: validated.url, | |
| title: validated.title ?? null, | |
| thumbnailUrl: validated.thumbnailUrl ?? null, | |
| memo: validated.memo ?? null, | |
| createdAt: now, | |
| updatedAt: now, | |
| } as TripUrlEntry); | |
| const id = await db.tripUrls.add({ | |
| tripId, | |
| url: validated.url, | |
| title: validated.title ?? null, | |
| thumbnailUrl: validated.thumbnailUrl ?? null, | |
| memo: validated.memo ?? null, | |
| createdAt: now, | |
| updatedAt: now, | |
| } as Omit<TripUrlEntry, 'id'>); |
| */ | ||
| export interface TripUrlEntry { | ||
| /** Dexie 自動採番の主キー */ | ||
| id: number; |
| async def _fetch_html(url: str) -> str | None: | ||
| """URL を fetch して HTML 文字列を返す。失敗時は None""" | ||
| try: | ||
| async with httpx.AsyncClient( |
| } | ||
|
|
||
| try: | ||
| client = genai.Client(api_key=api_key) |
| if log_path is not None: | ||
| logger.info("Gemini debug log: %s", log_path) | ||
| return formatted or None | ||
| except Exception as exc: # SDK が投げる例外は多岐に渡るため広めに捕捉 |
旅程ページ編集モードに、参考にしたい外部 URL を memo 付きで
ストックできる機能を追加。MVP 検証中は DB マイグレーションを避け、
永続化はフロント側 IndexedDB のみで行う。
サーバー(ステートレス、永続化なし)
- POST /trips/{trip_id}/urls/preview: URL から title / og:image を取得
- POST /trips/{trip_id}/urls/format: 貼付テキスト + 指示を Gemini で
GitHub Flavored Markdown に整形
- 依存追加: trafilatura / httpx / google-genai
- GEMINI_API_KEY を dotenvx 暗号化で .env / .env.stg / .env.prod に追加
フロント
- AddTripUrlDialog / EditTripUrlDialog: URL + memo(markdown)の自由記述
- TripUrlFormatSection: 整形対象テキスト + 指示 → AI 整形結果を memo 末尾に追記
- TripUrlList / TripUrlListItem: 一覧表示(編集モードのみ)
- IndexedDB (Dexie v3) の tripUrls テーブルで CRUD を実装
- Gemini デバッグキャッシュ: local 既定 ON / stg・prod 常時 OFF。
?debug=true で Jotai atom を立てて SPA セッション中は強制リフレッシュ、
HIT 時は console.warn でログ出力
Refs #117
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c4831f8 to
660961e
Compare
|
Visit the preview URL for this PR (updated for commit 660961e): https://tabi-share-8ef6b--pr123-feature-issue117-url-gd755zd5.web.app (expires Sun, 17 May 2026 06:22:59 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 9f2a87ede127df7673322845e34cf22c1372d720 |
Summary
/preview(メタ取得)と/format(Gemini で markdown 整形)の ステートレス 2 本のみ。永続化はしないtripUrlsテーブル)に保存し、DB マイグレーションを回避主要な変更
Backend (
server/)routers/trip_urls.py:POST /trips/{trip_id}/urls/preview//formatservices/url_extractor.py:extract_url_metadata+format_text_with_gemini(システムプロンプト固定)schemas/trip_url.py: I/O スキーマのみ。永続化用モデル・migration なしconfig.py:GEMINI_API_KEY/gemini_model設定(gemini-2.5-flash-lite)trafilatura/httpx/google-genai/tmp/tabishare-gemini-debug/*.jsonへ書き出しFrontend (
frontend/)dialogs/AddTripUrlDialog.tsx: URL + memo(markdown)+ AI 整形セクションdialogs/EditTripUrlDialog.tsx: 編集 + AlertDialog 経由の削除components/tripUrl/TripUrlFormatSection.tsx: 整形対象 + 指示 → AI 結果を memo 末尾に追記components/tripUrl/TripUrlList(Item).tsx: 編集モード時のみ表示hooks/useTripUrls.ts: CRUD は IndexedDB、/preview/formatはサーバー APIlib/db.ts: Dexie v3 にtripUrls: '++id, tripId, createdAt'追加lib/debugCache.ts: Gemini レスポンスのデバッグキャッシュ汎用ユーティリティ?debug=trueで Jotai atom を立てて強制リフレッシュconsole.warnでログpages/TripPage.tsx: 編集モードに<TripUrlList tripId={trip.id} />を統合設計メモ(議論ログ)
Test plan
cd server && pnpm dotenvx run -- uv run pytest(既存 91 件パス済)cd frontend && pnpm test(既存 72 件パス済)?debug=trueで同一入力でも Gemini が再実行される(HIT ログが消える)?debug=falseまたは param なしリロードで通常キャッシュ動作に戻る既知のスコープ外
🤖 Generated with Claude Code