Skip to content

Commit edb390a

Browse files
committed
管理画面にGitHubリポジトリからのスポット種別一括取り込みを追加
- admin専用セクション「GitHubリポジトリから取り込み」。リポジトリ (owner/リポジトリ名、既定rtcode337/travel-log-data)のcatalog.jsonから スポット種別の一覧を取得して表示し、選んだ種別のsettings.json・ spots.csv・excluded_candidates/exclude.txt・routes.csvを順に適用する (種別が無ければ作成、あれば上書き。exclude.txtの削除は件数確認あり) - spots.csv・routes.csvの差分インポート本体を共通関数に切り出し、 既存の個別インポートはその薄いラッパーにした - 一括取り込みのAPI呼び出しにリトライを追加(一時的な失敗1回で 数千件のインポート全体が中断しないように) - next devのファイル監視からdb/data・photosを除外。Postgresの書き込みの たびに再コンパイルが走り、処理中のリクエストが壊れて連続リクエストの インポートが途中失敗していた問題の根本対処 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 688e646 commit edb390a

4 files changed

Lines changed: 539 additions & 68 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,18 @@ GitHub Actions(`.github/workflows/docker-publish.yml`)がビルド時に`<JST日
170170

171171
### キー一覧を指定しての削除
172172

173-
`/[type]/admin`のadmin専用セクション「キー一覧を指定して削除」(`app/api/spots/delete-by-keys/route.ts`、POST `?type=`+ボディ`{ keys: string[], dryRun?: boolean }`)。`spots.key`を1行1つ書いたテキストファイルをアップロードすると、クライアント側(`AdminView`の`handleDeleteKeysFile`)がファイル本文をキー一覧にパースし、一致する**公開スポットのみ**を削除する。travel-log-data側で「場所ではない記事」等をCSVから外した際に、その`key`の一覧(`<スポットキー>/*_excluded_candidates/exclude.txt`)をそのままアップロードしてDB側も追随させるための機能 — **CSVインポートは差分更新でCSVに無い行に触らないため、行を消しただけではDBから消えない**。exclude.txtをコピペせずファイルとして渡せるよう、テキストエリア貼り付けではなくファイルアップロードにしている(スポット種別JSONの反映と同じUI)。
173+
`/[type]/admin`のadmin専用セクション「キー一覧を指定して削除」(`app/api/spots/delete-by-keys/route.ts`、POST `?type=`+ボディ`{ keys: string[], dryRun?: boolean }`)。`spots.key`を1行1つ書いたテキストファイルをアップロードすると、クライアント側(`AdminView`の`handleDeleteKeysFile`)がファイル本文をキー一覧にパースし、一致する**公開スポットのみ**を削除する。travel-log-data側で「場所ではない記事」等をCSVから外した際に、その`key`の一覧(`<スポットキー>/excluded_candidates/exclude.txt`)をそのままアップロードしてDB側も追随させるための機能 — **CSVインポートは差分更新でCSVに無い行に触らないため、行を消しただけではDBから消えない**。exclude.txtをコピペせずファイルとして渡せるよう、テキストエリア貼り付けではなくファイルアップロードにしている(スポット種別JSONの反映と同じUI)。
174174

175175
- 空行と`#`で始まる行(除外リストのコメント)はクライアント側で読み飛ばす
176176
- **一致しないキーはエラーにせず`notFoundKeys`として返すだけ**。除外リストは追記していく運用で、既に消したキーが毎回含まれるため(毎回ファイル全体を貼れる)
177177
- 一致は`key`が第一だが、**keyが未設定(null)の既存行に限り`name`の完全一致でも拾う**。keyを振る前に取り込んだデータ(観光地の初回投入分など)をキー一覧だけで掃除できるようにするための後方互換で、travel-log-data側のkeyは「スポット名(重複時のみ連番サフィックス)」の規則のため名前で引ける
178178
- `dryRun: true`で件数・該当なしキー・対象名のサンプル20件だけを返す。UIはファイルを選んだ時点でこのdryRunを自動実行し、その結果(削除対象の件数)を確認してからでないと削除ボタンを出さない
179179
- 全削除(purge)と同様、他ユーザーの訪問記録・写真を巻き込むためadmin専用(spot_adminは不可)。写真ファイルもvisitsがカスケードで消える前に集めて削除する
180180

181+
### GitHubリポジトリからの一括取り込み
182+
183+
`/[type]/admin`のadmin専用セクション「GitHubリポジトリから取り込み」(`AdminView`の`handleGithubOpen`/`handleGithubApply`)。リポジトリ(`owner/リポジトリ名`、既定`rtcode337/travel-log-data`)を入力して「開く」を押すと、`raw.githubusercontent.com`(mainブランチ固定、CORS可)からブラウザが直接リポジトリ直下の`catalog.json`(`{ "spot_types": [ { "key", "label" }, ... ] }`形式のスポット種別カタログ)を取得して一覧表示する(既存種別かどうかを「上書き」「新規作成」バッジで示す)。一覧から種別を選んで「適用」すると、そのフォルダの `<キー>/settings.json`・`<キー>/spots.csv`・`<キー>/excluded_candidates/exclude.txt`・`<キー>/routes.csv` を取得し、この順に適用する。settings.jsonだけは必須(無ければ中止)で、他は無ければスキップ。種別が無ければ作成し、あればlabel・設定・シリーズ・カテゴリを上書きする(`applyTypeDefinition`。settings.jsonのkeyとフォルダ名の不一致は中止)。spots.csv・routes.csvは個別インポートと同じ差分更新ロジックを共通関数(`runSpotsCsvImport`/`runRouteCsvImport` — 個別インポートのハンドラもこれらの薄いラッパー)で対象種別に対して実行し、exclude.txtは「キー一覧を指定して削除」と同じAPIで削除件数の確認ダイアログにOKしたときだけ実行する(キャンセルしても後続のroutes.csvは続行)。routes.csvの検証用スポットはspots.csv適用後に取り直す。バックエンドに専用エンドポイントは無く、既存APIの組み合わせのみ。
184+
181185
### 登録経路(`spots.origin`)とtravel-log-dataへの還元用エクスポート
182186

183187
アプリ利用中に抜け漏れに気づいて画面から手動追加したスポットを、travel-log-data側のCSVへ還元(逆輸入)するための仕組み。`spots.origin`(`'csv'`/`'manual'`、既定`'manual'`)が登録経路を記録し、CSVインポート(`AdminView`)だけがPOST/PATCHで`'csv'`を明示する(APIは`'csv'`の指定をspot_admin/adminに限定)。`spots.key`の有無で代用しない — keyはルート参照用で、手動スポットにkeyを振る将来の機能と両立しないため。既存データはマイグレーション`003_spot_origin_and_deletions.sql`が「key有り=csv」で一度だけ近似した。

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ docker compose pull && docker compose up -d
137137
|---|---|
138138
| `/[type]/map` | 地図(ホーム)。シリーズ・訪問状態・カテゴリでフィルタ。ピンタップ→スポット詳細モーダルへ。別の種別のダウンロード済み公開スポットを半透明で重ねて表示することもできる(タップで読み取り専用の詳細を表示)。左下に今表示中のスポット種別名を小さく表示 |
139139
| `/[type]/spots` | 「都道府県から探す」(地域別ドリルダウン)と「シリーズから探す」(検索+絞り込み+ページング)の2タブ |
140-
| `/[type]/admin` | (管理者・スポット管理者専用)スポットの承認待ちキュー・追加・編集・削除・CSVインポート・ルート(巡った順の矢印)のインポート・travel-log-dataへの還元用エクスポート(手動追加した公開スポットと画面から削除したCSV由来スポットの一覧をMarkdownでダウンロード)。adminのみキー一覧を指定しての削除・スポット種別の管理・ユーザー管理も可能 |
140+
| `/[type]/admin` | (管理者・スポット管理者専用)スポットの承認待ちキュー・追加・編集・削除・CSVインポート・ルート(巡った順の矢印)のインポート・travel-log-dataへの還元用エクスポート(手動追加した公開スポットと画面から削除したCSV由来スポットの一覧をMarkdownでダウンロード)。adminのみキー一覧を指定しての削除・GitHubリポジトリ(travel-log-data形式)からの一括取り込み・スポット種別の管理・ユーザー管理も可能 |
141141
| `/[type]/account` | 自分のロール表示、ログアウト、他のスポット種別への切り替え |
142142
| `/login` | メールログイン、または Google でログイン(任意、要設定) |
143143

0 commit comments

Comments
 (0)