公開
自分のアプリを自分で計測するポートフォリオの作り方
このポートフォリオ自体の設計。npm registry・OSV・GitHub API から各アプリの状態を集め、ビルド時に静的化して Workers の CPU 上限を避けるまでの経緯と、セキュリティ列とバッジの意味の違い。
このサイトは個人開発したアプリの一覧ですが、「作品を並べる」だけでなく「各アプリの状態を自動で計測して載せる」ことを目的に作っています。このブログの最初の記事として、ポートフォリオ自身の作りを書いておきます。
何を自動で集めているか
アプリの定義は src/lib/projects/data.ts に集約していて、それぞれに trackedPackages(監視する npm パッケージ)と GitHub の URL を持たせています。ビルド時に Next.js の Server Components が次の 3 つを取りに行きます。
| データ | 取得元 | 用途 |
|---|---|---|
| 最新バージョン | npm registry | 各アプリの依存が outdated かどうか |
| 脆弱性 | OSV API | 依存の宣言レンジに既知の CVE があるか |
| 最終コミット日 | GitHub API | public リポジトリの更新状況 |
一方で Lighthouse・Mozilla Observatory・テスト件数・npm audit の結果は、計測のたびに手で data.ts に書き写しています。こちらは「計測日」を必ず添えて、古い数字が新しい数字に見えないようにしています。
なぜトップページを静的ファイルにしたか
本番は Cloudflare Workers(@opennextjs/cloudflare)です。当初はリクエストごとに上の 3 つを取得してレンダリングしていましたが、無料プランの CPU 上限を超える 1102 エラーが出るようになりました。2026-10-06 の PR #55 で、/・/icon.svg・/opengraph-image・/api/og/<id>.png を next build で事前生成し、scripts/export-static.mjs が public/ にコピーして Workers の静的アセットとして配る形にしました。静的アセットは Worker を起動せずに返るので、CPU 時間を消費しません。
代わりにデータの鮮度はビルド時点になります。そこで GitHub Actions の rebuild.yml が 3 時間ごとに Workers Builds の Deploy Hook を叩き、再ビルドしています。画面右上の「取得 <日時>」はこの事情を伝えるためのもので、更新ボタンは置いていません(SHIG 55)。
セキュリティヘッダーは src/lib/security-headers.ts を正本にして、Worker が返す応答(next.config.ts の headers())と静的アセット(public/_headers)の両方に同じ値を書き出しています。片方だけ直すと食い違うためです。
セキュリティ列とバッジは別物
一覧の「Security」列とバッジは、同じ「脆弱性」を指しているように見えて計算が違います。
- Security 列: 各リポジトリで
npm audit --omit=devを手動実行した結果。本番依存だけを対象にし、score = 100 - 25×critical - 10×high - 3×moderate - 1×lowで算出。開発依存だけの勧告はnotesに書く - バッジ:
trackedPackagesの宣言レンジを OSV にライブ照会した結果。宣言の下限バージョンで判定するため、実際にインストールされている版より古く見えることがある(既知の限界)
CI 側では、素の npm audit の代わりに scripts/audit-gate.mjs を回しています。audit-allowlist.json にない勧告があれば失敗し、例外には理由と期限(約 1 か月先)が要ります。例外に修正版が出た場合も失敗にして、放置できないようにしています。
詳細モーダルと URL
カードを開くと詳細モーダルが出ますが、その状態は ?work=<id> としてURLに載せています。ブラウザの戻るで閉じられ、特定の作品を共有できるためです(SHIG 59・60・82)。このブログの記事からも同じ URL でアプリの詳細へ飛べるようにしました。
参考
- tktk7l9/my-apps-portal — README と AGENTS.md(静的化の経緯、audit gate の仕様)
- PR #55「トップページとアイキャッチ画像を静的アセットで配信し、Workers の CPU 上限超過(1102)をなくす」
- PR #46「npm audit を例外リストつきのゲートに置き換える」