Skip to content

feat(functions): 残りのHTTPエンドポイントをGoへ移植(sanpai/ranking/register/ogpRewrite)#127

Merged
cursor[bot] merged 10 commits into
alpha-0.2from
cursor/go-sanpai-endpoint-5520
Jul 1, 2026
Merged

feat(functions): 残りのHTTPエンドポイントをGoへ移植(sanpai/ranking/register/ogpRewrite)#127
cursor[bot] merged 10 commits into
alpha-0.2from
cursor/go-sanpai-endpoint-5520

Conversation

@kojira

@kojira kojira commented Jul 1, 2026

Copy link
Copy Markdown
Member

概要

statusエンドポイントのGo移植(#126)に続き、ユーザーが直接待つ残りのHTTPエンドポイントをすべてGo(Cloud Run functions)に移植します(それぞれ別関数名でデプロイし、既存のNode版と共存)。

  • sanpaisanpaiGo(参拝処理。Firebase認証・GitHub API・Firestore書き込み)
  • rankingrankingGo(ランキング表示)
  • registerregisterGo(ユーザー登録)
  • ogpRewriteogpRewriteGo(プロフィールページのOGPメタタグ書き換え。Firebase Hostingの/u/*リライト経由で全プロフィール閲覧のたびに呼ばれるため、実はstatusと並んで体感速度に直結する)

挙動はNode版と同一にすることを優先し、独自の改善は入れていません。

移植を見送った機能とその理由(詳細はdocs/backend.md「Go移植を見送った機能」参照):

  • userOGP(OGP画像生成): canvas/chartjs-node-canvasによるサーバーサイド画像合成に強く依存しており、Goで同等の結果を得るには画像処理を一から実装し直す必要がある(他のエンドポイントとは技術的難易度が大きく異なる)。またページの初期表示をブロックしない経路のため、レイテンシー改善の優先度は相対的に低い。
  • スケジュール関数(rankingUpdate/rankingCache/statusCacheBackfill/scheduledOgpDelete): ユーザーが待つものではないためコールドスタート短縮の恩恵がなく、Pub/Subトリガーの構成変更(専用のCloudEventハンドラ・Cloud Scheduler設定)が別途必要になるため対象外とした。

第三者視点のレビューを2回実施済み(1回目: sanpai部分、2回目: ranking/register/ogpRewrite部分。指摘事項は全て対応済み)。

主な変更

sanpai → sanpaiGo

  • app/functions-go/internal/performance/: 増分計算ロジック(RawUserDataFromStatus/ComputePerformanceIncrement/LatestActivityCreatedAt)をポート。全件計算と増分計算の一致をプロパティテスト(2000/1000ケース)で検証
  • app/functions-go/sanpai.go: Firebase Admin Go SDKでのID検証、GitHub Events API呼び出し、クールダウン判定、増分/フル計算の切替、Firestore書き込みを実装
  • app/functions-go/sanpai_test.go: Firestoreエミュレータ+モックGitHub APIでの統合テスト、HTTPレベルの分岐テスト

ranking → rankingGo

  • app/functions-go/ranking.go: cache_data/ranking_cacheから上位100件+自分の順位を返す。latest_updateはNode版のFirestore Timestamp JSON化形式(_seconds/_nanoseconds)を再現

register → registerGo

  • app/functions-go/register.go: Firebase IDトークン検証、新規作成/auth_user_uid補完/登録済みの3分岐

ogpRewrite → ogpRewriteGo

  • app/functions-go/ogp_rewrite.go: SPAのビルド済みHTMLを取得しOGP/Twitterカード用メタタグを文字列置換

共通

  • .github/workflows/dev-deploy.yml: 各関数のデプロイステップを追加。GitHub OAuth資格情報・func.base_urlは新規Secretsを増やさず、既存のfirebase functions:config:getから取得しマスクした上で環境変数として渡す
  • decodeJSONBody共通ヘルパーを追加し、sanpai/registerとも不正なJSONボディの場合にNode版(Expressのbody-parser)と同じ順序・ステータスコード(400、メソッド判定・認証チェックより前)で応答するよう統一
  • docs/backend.md / app/functions-go/README.md: 設計・既知の挙動・見送った機能の理由をドキュメント化

既存Node版からそのまま引き継いだ、あえて修正していない挙動

詳細はdocs/backend.mdを参照。主なもの:

  • sanpai: IDトークンのuidgithub_idの一致チェックなし、exp表示キャッシュの非原子性、GitHub取得失敗時のレスポンス
  • register: POST以外は400を返す(sanpai/statusは同状況で200を返すNode版の既存の不整合をそのまま踏襲)
  • ogpRewrite: /u/を含まないパスへの直接アクセス時、Node版の未到達コードパスでの例外化(500)をそのまま踏襲

第三者レビュー(2回目)で見つかり対応した点

  • register: 不正なJSONボディをGo版が握りつぶしたまま処理を続行していたのを、Node版と同じ順序・ステータスコード(400)で応答するよう修正
  • ranking: ranking_cacheドキュメントにrankingフィールドが欠落する異常系で、Go版が{"ranking":null}を200で返し壊れたキャッシュを黙って正常応答していたのを、internal error(500)にするよう修正
  • テストカバレッジ不足(認証・ボディパースのHTTPレベル分岐、ogpRewriteの置換パターン、rankingのJSON形状)を補うテストを追加

テスト

  • go build ./... / go vet ./... / go test ./...(いずれもOK)
  • FIRESTORE_EMULATOR_HOSTを設定してのFirestoreエミュレータ統合テスト(全エンドポイント27件パスOK)
  • actionlintでCIワークフローの構文チェック(OK)
  • 第三者視点でのコードレビューを2回実施(重大な問題なし、指摘事項は全て対応済み)

未実施

  • devデプロイ後のテスト専用ユーザーでの実機検証(Node版との入出力比較)
  • フロントエンドの呼び出し先切替(statusGoと同様、安定稼働確認後に別途実施)
Open in Web Open in Cursor 

cursoragent and others added 8 commits July 1, 2026 11:16
- RawUserDataFromStatus / ComputePerformanceIncrement / LatestActivityCreatedAt を
  Node版(performance.js)と同一ロジックでポート
- 全件計算と増分計算の一致をランダム生成データで検証するプロパティテストを追加
  (2バッチ2000ケース、3バッチ1000ケース)

Co-authored-by: kojira <kojira@users.noreply.github.com>
- Firebase Admin Go SDK(auth)でIDトークンを検証
- GitHub Events APIを取得し、Node版と同一のロジックで参拝処理を実装
  (クールダウン判定・ボーナスブランチ加点・増分/フル計算の切替・
  Firestoreへのアクティビティ/ログ/ステータス書き込み)
- 別関数名 sanpaiGo としてデプロイし、既存の sanpai(Node) と共存させる
- Firestoreエミュレータ + モックGitHub APIサーバーによる統合テストを追加
  (FIRESTORE_EMULATOR_HOST未設定時は自動スキップしCIに影響しない)

Co-authored-by: kojira <kojira@users.noreply.github.com>
- firebase functions:config:get で既存のGitHub OAuth資格情報を取得し、
  ::add-mask:: でマスクした上で gcloud functions deploy の環境変数として渡す
  (新規のGitHub Secretsは追加しない)
- クールダウン秒数は SANPAI_NEXT_TIME_SECONDS 環境変数でdev/prodを明示的に指定
- docs/backend.mdとfunctions-go/READMEにsanpai移植の設計・既知の挙動を追記

Co-authored-by: kojira <kojira@users.noreply.github.com>
- 増分計算パスでGitHubイベントのpayloadを引き継ぐよう修正
  (IssuesEvent判定は文字列一致のみで実害はないが、フル計算パスとの
  一貫性のため)
- 季節ボーナス(2022年正月キャンペーン)を意図的に未移植とする理由を
  コード・docsに明記
- GitHub API呼び出しのクライアント側タイムアウトを撤廃しNode版(axios)に
  合わせる(独自改善を入れない方針のため。上限はCloud Run functionsの
  リクエストタイムアウトで担保)
- GitHubレート制限ヘッダーのロギングを追加(Node版と同様の運用監視情報)
- 不正なJSONボディ時のステータスコード差異をdocsに明記
- go mod tidyでfirebase-admin/grpcの直接依存マーキングを修正

Co-authored-by: kojira <kojira@users.noreply.github.com>
- cache_data/ranking_cacheを読み、上位100件+自分の順位をNode版と同一形式で返す
- latest_updateはNode版がFirestore Timestampをそのままjson化した際の
  {_seconds,_nanoseconds}形式を再現
- screen_name未指定/該当なしの場合はmy_rankキー自体を省略(Node版と同じ)
- Firestoreエミュレータによる統合テストを追加

Co-authored-by: kojira <kojira@users.noreply.github.com>
- Firebase Admin Go SDKでIDトークンを検証
- 新規ユーザー作成/auth_user_uid補完/登録済みの3分岐をNode版と同一ロジックで実装
- Firestoreエミュレータによる統合テストを追加

Co-authored-by: kojira <kojira@users.noreply.github.com>
- SPAのビルド済みHTML(base_url)を取得しOGP/Twitterカード用メタタグを
  Node版と同一の文字列置換で書き換え
- base_url/プロジェクトIDはデプロイ時の環境変数として明示的に注入
- モックHTTPサーバーによるテストを追加(置換内容・Cache-Controlヘッダー・
  パス不一致時の挙動を検証)

Co-authored-by: kojira <kojira@users.noreply.github.com>
- func.base_urlもGitHub資格情報と同様にfirebase functions:config:getから取得し
  ogpRewriteGoの環境変数として渡す
- docs/backend.mdにuserOGP(画像生成)とスケジュール関数を今回のGo移植対象外と
  した理由を明記

Co-authored-by: kojira <kojira@users.noreply.github.com>
@cursor cursor Bot changed the title feat(sanpai): 参拝処理エンドポイントをGoへ移植(sanpaiGo) feat(functions): 残りのHTTPエンドポイントをGoへ移植(sanpai/ranking/register/ogpRewrite) Jul 1, 2026
cursoragent and others added 2 commits July 1, 2026 12:02
- register: 不正なJSONボディ時、Node版(Expressのbody-parser)と同じ順序
  (メソッド判定・認証チェックより前)でパースし400を返すよう修正。
  sanpaiも同様に統一(共通ヘルパー decodeJSONBody を追加)
- ranking: cache_data/ranking_cache に ranking フィールドが欠落している
  異常系で、Go版が {"ranking":null} を200で黙って返していたのを
  internal error(500)にするよう修正。Node版は同状況で例外化するため、
  Go版が壊れたキャッシュを正常応答として隠してしまわないようにする
- テストカバレッジ不足の指摘に対応:
  - registerHandler/sanpaiHandlerのHTTPレベル分岐(メソッド判定・認証
    ヘッダー欠落・不正ボディ)のテストを追加
  - ogpRewriteのog:descriptionのもう一方の置換パターン(property属性
    なし)とtwitter:imageの明示的な検証を追加
  - rankingのレスポンスJSONに実際に_seconds/_nanosecondsキーが出力
    されることを検証するテストを追加
- docs/backend.mdを更新

Co-authored-by: kojira <kojira@users.noreply.github.com>
- sanpai → sanpaiGo (web/pages/sanpai.vue)
- register → registerGo (web/pages/index.vue)
- ranking → rankingGo (web/components/ranking.vue)
- ogpRewrite → ogpRewriteGo (app/firebase.json のHosting rewrite)

いずれもNode版とレスポンス形式は同一(docs/backend.md参照)。既存のNode版
function自体は当面残し、切替のみ行う(即座にロールバックできるようにするため)。
statusGoの切替(ae502ab)と同じ方針。

Co-authored-by: kojira <kojira@users.noreply.github.com>
@cursor
cursor Bot merged commit 6b536f0 into alpha-0.2 Jul 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants