Trackable Links 仕様書
1. 概要
QRコードによるリンク追跡・転送システム。設置場所ごとのアクセスログを記録し、分析に活用する。
1.1 ユースケース図
1.2 ドメイン
- API:
fwd.soshosai.com(QR転送・管理API)
1.2 アーキテクチャ
2. 機能
2.1 QR転送機能
QRコードをスキャンすると、設定された転送先URLにリダイレクトする。
フロー:
- QRコードスキャン →
fwd.soshosai.com?id={qrId} - QRコード情報をDBから取得
- 場所未設定の場合 → 場所設定画面を表示
- アクセスログを記録
- 転送先URLにリダイレクト(301)
2.2 プロジェクト管理
プロジェクト単位でQRコードを管理する。
| 項目 | 説明 |
|---|---|
| プロジェクト | QRコードのグループ。転送先URLを持つ |
| QRコード | 個別のQRコード。場所情報を持つ |
| アクセスログ | QRスキャン時の記録 |
2.3 LINE Bot連携
LIFFを使用したQRスキャナー機能を提供。
2.4 Discord 投稿からの設置場所登録(ポスター巡り)
ポスター巡りで Discord の対象チャンネル(03-ポスター巡り とその配下のスレッド)に投稿された
「QR コードの写真」と「設置場所」を突き合わせ、QR コードの設置場所を自動で登録する。
- 受信:
gakusai-assistant-workerの cron(1 分ごと)が Discord REST で新着を取り込む。Gateway の常時接続は使わない - QR 読み取り: 添付画像を Discord CDN の縮小で小さくしてから Worker 内で zxing-wasm にかける
- 突き合わせ: 「写真と場所が同じ投稿」「場所テキスト → 写真」「写真 → 場所テキスト」「写真へのリプライ」の 4 形を、同一投稿者・同一チャンネル・10 分以内・リプライ参照で結び付ける。本文の解釈(場所か雑談か)は LLM が一次判定し、失敗時はルールに落ちる
- 登録: 本サービスの Service Binding RPC
TrackableLinksRpc.setQrLocation(qrId, location, actorDiscordId)でQRCodes.locationを更新する(HTTP には露出しない。JWT を持たない Bot からの唯一の書き込み経路) - 結果は Discord のリアクションで返す(✅ 登録 / ⚠️ 一部の QR が未登録 / ❌ 失敗。QR が見つからない写真には何も返さず、画像の取得・変換に失敗したときだけ ❌ を返す)。登録済みの写真へのリプライは上書き(訂正)として扱う
詳細は packages/api/gakusai-assistant-worker/src/poster/README.md を参照。
3. エンドポイント
3.1 転送系(認証不要)
| Method | Path | 説明 |
|---|---|---|
| GET | /?id={qrId} | QR転送(メイン機能) |
| GET | /view/:qrId | QRコード情報表示 |
| GET | /linebot/scanner | LIFFスキャナー画面 |
3.2 プロジェクト管理系(スタッフ認証必須)
| Method | Path | 説明 |
|---|---|---|
| GET | /projects | プロジェクト一覧 |
| POST | /projects/create | プロジェクト作成 |
| GET | /projects/:id | プロジェクト詳細 |
| PUT | /projects/:id | プロジェクト更新 |
| DELETE | /projects/:id | プロジェクト削除 |
| GET | /projects/:id/getAccessLogs | アクセスログ取得 |
3.3 QRコード管理系(スタッフ認証必須)
| Method | Path | 説明 |
|---|---|---|
| GET | /projects/getQRCode | QRコード一覧 |
| GET | /projects/getQRCode/:id | QRコード詳細 |
| POST | /projects/createQRCode | QRコード作成 |
| PUT | /projects/updateQRCode/:id | QRコード更新 |
| DELETE | /projects/deleteQRCode/:id | QRコード削除 |
3.4 場所設定系(スタッフ認証 + パスコード)
| Method | Path | 説明 |
|---|---|---|
| POST | /api/set-location | 場所設定 |
| POST | /api/edit-location/:qrId | 場所編集 |
3.5 Service Binding RPC(HTTP 非公開)
| Entrypoint | Method | 呼び出し元 | 説明 |
|---|---|---|---|
TrackableLinksRpc | setQrLocation(qrId, location, actorDiscordId) | gakusai-assistant-worker | QR コードの設置場所を更新する |
4. データベース
データベース名: soshosai-qr-forwarder
4.0 ER図
4.1 Projects
CREATE TABLE Projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
destination_url TEXT NOT NULL,
admin_user_id TEXT,
created_at TEXT NOT NULL
);
4.2 QRCodes
CREATE TABLE QRCodes (
id TEXT PRIMARY KEY, -- UUID
project_id TEXT NOT NULL,
location TEXT, -- 設置場所
creator_id TEXT, -- 作成者のDiscord ID
created_at TEXT NOT NULL,
FOREIGN KEY (project_id) REFERENCES Projects(project_id)
);
4.3 AccessLogs
CREATE TABLE AccessLogs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id TEXT NOT NULL,
qr_id TEXT NOT NULL,
accessed_at TEXT NOT NULL,
user_agent TEXT,
ip_address TEXT
);
5. 認証
5.1 スタッフ認証
/projects 以下のエンドポイントはスタッフ認証が必須。
認証フロー:
soshosai_staff_jwtCookieから JWT を取得unified-auth-workerで JWT を検証userType === 'staff'を確認
5.2 パスコード認証
場所設定時は追加でパスコード認証が必要。
const expectedPasscode = c.env.LOCATION_SETUP_PASSCODE;
if (passcode !== expectedPasscode) {
return c.json({ message: 'パスコードが正しくありません' }, 401);
}
6. 環境変数
| 変数名 | 説明 |
|---|---|
D1_DB | D1 Database Binding |
AUTH_SERVICE | unified-auth-worker Binding |
LOCATION_SETUP_PASSCODE | 場所設定用パスコード |
LIFF_ID | LINE LIFFアプリID |
LOGGER | ログサービス Binding |