Skip to content

NUTFes/TrackingLink

 
 

Repository files navigation

TrackingLink

QRコード/リンクのアクセスを記録し、リダイレクトするためのセルフホスト型トラッキングツールです。ポスターやチラシに印刷したQRコードがいつ・どこでスキャンされたかを記録し、管理画面で確認できます。

trackable-links-oss(MITライセンス)をベースに、NUTFesの文化祭運用向けにリブランド・改修したものです。

できること

  • プロジェクト単位で、同じリダイレクト先URLを持つQRコードをグループ化(例: チラシA、ポスターBなど)
  • QRコードには設置場所ラベルを付けられる(例: 「正門」「2号館前」)。印刷直後のQRコードは場所未設定の状態で、初回スキャン時にパスコード入力付きのフォームが表示され、設置した人がその場で場所を登録できる
  • 場所登録後は、スキャンのたびにアクセスログ(日時・User-Agent・IP)を記録し、301リダイレクトで指定URLへ遷移
  • 管理画面でプロジェクトごとのスキャン数、QRコード管理(作成/印刷/削除)、時間帯×場所別の分析を確認可能
 QRコードをスキャン ──▶ GET /?id={qrId} (packages/api) ──▶ ログ記録 ──▶ 301リダイレクト
                                │
                                ▼
                      D1データベース (Projects, QRCodes, AccessLogs)
                                ▲
                                │
        管理画面 (packages/web) ──▶ /projects/*, /auth/* (Bearer JWT)

構成

このリポジトリはpnpmワークスペースで、2つの独立したアプリケーションから成ります。デプロイ先が異なる点に注意してください。

  • packages/apiHono製のWorker。QRコードのリダイレクト、場所設定、認証付きのプロジェクト/QRコード管理APIを提供。データはCloudflare D1(Drizzle ORM経由)に保存。 → Cloudflare Workersにデプロイ済み(下記「本番API」参照)
  • packages/web — React + Vite + Tailwindの管理画面(SPA)。fetchでAPIと通信する。 → Cloudflareにはデプロイせず、自作サーバーでホストする方針

本番API(デプロイ済み)

項目
Worker URL https://trackinglink.nutfes-nutmeg9488.workers.dev
Cloudflare Workerプロジェクト名 trackinglink
D1データベース名 trackinglink-db
デプロイ方法 Cloudflareダッシュボード連携(Workers Builds)。mainブランチにpushすると自動デプロイ

APIの設定・認証情報は以下の2種類に分かれています。

  • packages/api/wrangler.jsonc(リポジトリにコミット): Worker名、D1バインディング(database_id含む)、ALLOWED_ORIGINSなど。database_idはリソースの識別子であり、それ単体では中身にアクセスできないため許容してコミットしています。
  • Cloudflareダッシュボード側でのみ設定(リポジトリには含まれない):
    • account_id → Worker の Settings → Build → Environment variablesCLOUDFLARE_ACCOUNT_ID として設定
    • JWT_SECRET / ADMIN_PASSWORD / LOCATION_SETUP_PASSCODE → Worker の Settings → Variables and Secrets に Secret(暗号化)として設定

Web(管理画面)のセットアップ

Webは静的サイト(SPA)としてビルドされ、Cloudflareとは別のサーバーで配信します。ローカル開発でも自作サーバーでの本番運用でも、同じ本番API(https://trackinglink.nutfes-nutmeg9488.workers.dev)を参照する構成にしています。

前提

  • Node.js 20以上
  • pnpm(未インストールの場合は npx pnpm@9.15.0 <コマンド> のように npx 経由でも実行できます)

1. リポジトリのclone・依存関係のインストール

git clone git@github.com:NUTFes/TrackingLink.git
cd TrackingLink
pnpm install

ワークスペース全体(packages/apipackages/web)の依存関係が一括でインストールされます。

2. ローカルで開発する場合

pnpm --filter @tracking-link/web dev

http://localhost:5173 で管理画面が起動し、packages/web/.env.local に設定された本番API(VITE_API_URL)へ接続します。ADMIN_PASSWORD(Cloudflareに設定した値)でログインしてください。

.env.localが無い場合は以下のように作成してください。

echo "VITE_API_URL=https://trackinglink.nutfes-nutmeg9488.workers.dev" > packages/web/.env.local

3. 自作サーバーへの本番デプロイ

本番ビルド時は packages/web/.env.production の値(既にリポジトリにコミット済み、VITE_API_URL=https://trackinglink.nutfes-nutmeg9488.workers.dev)が自動的に使われます。

pnpm --filter @tracking-link/web build

packages/web/dist/ に静的ファイル一式が出力されるので、これを自作サーバー上の任意のWebサーバー(nginx、Apache、Node製の静的サーバーなど)で配信してください。

注意: このアプリはReact Routerによるクライアントサイドルーティングを使用したSPAです。存在しないパス(/linksなど)へ直接アクセス・リロードされた場合に index.html を返すよう、SPAフォールバック設定をWebサーバー側で行ってください(例: nginxならtry_files $uri /index.html;)。

4. CORSの設定(重要・要対応)

自作サーバーのドメインが決まったら、packages/api/wrangler.jsoncALLOWED_ORIGINS に追加してcommit・pushしてください(pushすると自動で本番APIに反映されます)。

"vars": {
    "ALLOWED_ORIGINS": "http://localhost:5173,http://127.0.0.1:5173,https://<自作サーバーのドメイン>"
}

これを設定しないと、自作サーバー上のWebからAPIへのリクエストがブラウザ側でブロックされます(現時点ではドメイン未定のため、localhost分のみ許可されています)。

認証

外部の認証プロバイダは使わず、単一の共有パスワード(ADMIN_PASSWORD)方式です。POST /auth/loginでパスワードを渡すと、24時間有効なHS256 JWTが発行され、以降のリクエストでAuthorization: Bearer <token>として使用します。

認証ロジックは差し替えやすいよう1箇所にまとまっています(packages/api/src/auth/):

auth/
├── types.ts       # Verifier型 + Bindings/AuthUser/HonoEnv —全体が依存する契約
├── local.ts        # 組み込みの単一パスワード認証(signLocalSession/verifyLocalSession)
├── middleware.ts    # createAuthMiddleware(verifier) — VerifierをHonoミドルウェア化
└── index.ts        # 上記の再エクスポート

将来Auth0やClerk、独自SSOなどに差し替える場合は、(token, env) => Promise<{ sub, permissions } | null> というシグネチャの関数を実装し、createAuthMiddlewareに渡すだけで済みます。

権限(Permissions)

4つの独立したビットの組み合わせです(packages/api/src/permissions.ts):

ビット 権限内容
TRACKING_LINK_VIEW 1 プロジェクト・QRコードの一覧参照
TRACKING_LINK_EDIT 2 プロジェクト・QRコードの作成、自分が作ったQRコードの削除
TRACKING_LINK_ANALYTICS 4 分析画面の閲覧
TRACKING_LINK_DELETE 8 任意のプロジェクト・QRコードの削除

現在の単一管理者ログインは、常にこの4つ全てを付与します。

APIリファレンス

/projects/* 配下はすべて Authorization: Bearer <token> が必要です。

Method Path 認証 説明
GET /?id={qrId} 不要 QRコードスキャン。アクセスを記録してリダイレクト、または場所未設定なら設定フォームを表示
GET /view/:qrId 不要 QRコードの情報を人間が読める形で表示するページ
POST /api/set-location パスコード QRコードの設置場所を初めて登録する
POST /api/edit-location/:qrId パスコード QRコードの設置場所を再登録する
POST /auth/login 不要 ADMIN_PASSWORDをセッショントークンと交換する
GET /auth/me Bearer 現在のセッション情報を取得
GET /projects Bearer プロジェクト一覧(ページネーション、スキャン数付き)
POST /projects Bearer プロジェクトを作成
GET /projects/:id Bearer プロジェクト詳細
PUT /projects/:id Bearer プロジェクトを更新
DELETE /projects/:id Bearer + DELETE権限 プロジェクトを削除(QRコードも連動して削除)
GET /projects/:id/qrcodes Bearer プロジェクト内のQRコード一覧(ページネーション)
POST /projects/:id/qrcodes Bearer QRコードを作成
GET /projects/:id/access-stats Bearer 時間帯×場所別のスキャン数集計
GET /projects/:id/access-logs Bearer 生のスキャンログ(ページネーション)
GET /projects/qrcodes Bearer 全プロジェクト横断のQRコード一覧
GET /projects/qrcodes/:id Bearer QRコード単体の情報
PUT /projects/qrcodes/:id Bearer QRコードの設置場所を更新
DELETE /projects/qrcodes/:id Bearer + DELETE権限/自分が作成したもの QRコードを削除(スキャンログも削除)

含まれていない機能

元になった内部ツールの一部機能は、組織固有のインフラに依存していたため今回のプロジェクトには含まれていません。

  • LINE Bot / LIFF QRスキャナー — LINE内からQRコードをスキャンするWebhook連携
  • レシートプリンター連携 — Epson ESC/POSプリンターへ直接QRラベルを印刷する機能

必要になれば追加実装も可能です。

ライセンス

MIT — LICENSE参照。

About

Self-hosted QR code link tracker on Cloudflare Workers + D1

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 96.7%
  • CSS 3.0%
  • HTML 0.3%