HN 日本語サマリー

← 一覧へ戻る
Web開発

Show HN: ResolveHQ – Cloudflare Workers, D1, R2, および Queues 上に構築されたヘルプデスク

Show HN: ResolveHQ – A Helpdesk Built on Cloudflare Workers, D1, R2 and Queues (github.com)

61 pointsby mirza_rizvi21 コメント

要約

ResolveHQは、小規模サポートチーム向けのCloudflareネイティブでセルフホスト可能なヘルプデスクです。テナント分離された顧客、チケット、担当者、ステータス、優先度、タグ、全文検索を備えた共有受信トレイを実行できます。Cloudflareの各種サービスを活用し、メール処理、ファイル添付、AI支援応答、レポート機能などを提供します。

全文翻訳

ResolveHQ

[画像: CI] [画像: Release] [画像: License: source-available] [画像: Runs on Cloudflare Workers]

ResolveHQは、Cloudflareネイティブでセルフホスト可能な、小規模サポートチーム向けヘルプデスクです。

要件:CloudflareアカウントとCloudflare上のドメイン(Email Routing用)。小規模チーム向けには無料プランで動作します。

[画像: Deploy to Cloudflare]

スクリーンショット

3ペインの受信トレイ。内部メモとキューに入れられた返信を含むチケットスレッドが表示されています:

[画像: 受信トレイにチケットが開かれ、キューリストと会話スレッドが表示されている]

モバイルビューや顧客ページ、設定ページを含むその他のスクリーンショットは、docs/images/ にあります。

実行できること

  • テナント分離された顧客、チケット、担当者、ステータス、優先度、タグ、全文検索を備えた共有受信トレイを実行できます。
  • オーナーとしてサインアップし、チームメイトを招待し、組織を横断するワークスペーススイッチャーでオーナー/管理者/エージェントのロールを管理できます。
  • RFC 5322 に従ってメールを正しくスレッド化し、チケットをまたいだ件名のなりすましに耐性を持たせます。
  • Cloudflare Email Routing を通じてメールを受信し、Resend を通じて送信します。配信ステータス、リトライ、冪等性のある Webhook を備えています。送信された返信は、リンクされた添付ファイルを運び、デッドレターキューは耐久性があり回復可能なレコードに排出されます。
  • 検証および承認された R2 アップロードを通じて、チケットにファイルを添付できます。
  • 保存された返信、内部メモ、AIで下書きされた応答(オプトイン)、および楽観的バージョン競合処理を備えた応答性の高い3ペイン受信トレイで、より速く返信できます。
  • システムメールを通じてパスワードリセットや招待の受け入れを行います。これはチケットメールと同じプロバイダーシームで送信されます。
  • ナレッジベースの記事から公開ヘルプセンターを公開します。下書きはチームに非公開のままです。
  • チケットが開かれた、割り当てられた、ステータスが変更された、応答ターゲットを満たせなかった、顧客からの返信があった、または評価されたときに、署名付き Webhook を自身のサービスに送信したり、Slack チャネルにメッセージを送信したり、Telegram チャットにメモを送信したりできます。ペイロードには ID とステータスが含まれ、メッセージテキストは含まれません。
  • AIアシスタントをヘルプデスクに接続できます。Claude Code、Claude Desktop、Cursor は MCP を介して接続し、チケットを検索したり、スレッドを読んだり、キューのカウントを確認したり、顧客を検索したり、ナレッジベースを検索したりできます。意図的に読み取り専用です。返信、割り当て、または変更を行うツールはありません。
  • スクリプトから ResolveHQ を操作できます。管理者は、/api/v1 に対して機能するスコープ付き API キーを作成できます。これはオプションで有効期限を設定したり、特定の受信トレイに制限したりできます。キーは一度だけ表示され、作成した人以上の権限を持つことはできません。その人を降格させると、キーはその権限を即座に失います。
  • 顧客にどのように感じたかを尋ねます。返信がチケットを解決すると、メールの下部に3つの評価リンクが追加されます。1クリックで回答が記録され、オプションでコメントが続きます。サードパーティのアンケートサービスやトラッキングピクセルなしでセルフホストされ、スコアには常に回答数が表示されます。
  • チケットを特定の日時にスヌーズできます。理由も指定できます。作業キューから離れ、スリープ中は期限切れとしてカウントされず、顧客が返信した瞬間に早く戻ってきます。そのため、延期することは、覚えておくことへの賭けではなく、正直な行為です。
  • 勤務時間を考慮した応答ターゲットを設定します。優先度ごとに初回応答と解決のターゲットを設定し、自身のタイムゾーンで週次スケジュールと休日を定義し、[Overdue](期限切れ)および [Due-soon](間もなく期限切れ)キュー、さらに各チケットにバッジを表示します。ポリシーを作成するまで何も追跡されず、金曜日の午後に到着したチケットは土曜日の朝には遅れていません。
  • レポートでボリュームと応答速度を追跡し、任意のウィンドウを CSV にエクスポートし、ルールベースのオートメーションでトリアージを自動化します。
  • アプリ内での担当者への割り当てと顧客からの返信を通知し、ライトモードまたはダークモードで快適に作業できます。
  • 顧客に関するすべての保存済み情報を JSON としてエクスポートするか、キューに入れられたメールをキャンセルする耐久性があり再開可能なワークフローで削除します。
  • ワークスペース全体を持ち運べます。1クリックで、すべてのテーブルを改行区切り JSON として自身の R2 バケットにエクスポートします。大きなワークスペースでもタイムアウトせずに完了するように、スライスごとにエクスポートされ、テーブルごとのダウンロードとオプションの週次スケジュールが可能です。パスワード、キーハッシュ、Webhook シークレットは含まれません。
  • 自動回復:5分ごとの cron ジョブが停止したメールジョブをリトライし、ステージングデータと孤立したデータをクリーンアップします。
  • デプロイが実際に機能しているかを確認します。セットアップページが設定を検証し、受信トレイ ドメインの MX、SPF、DKIM、DMARC レコードをルックアップし、不足しているものがあれば変更点を通知します。
  • ワークスペースごとに AI アシスタントを制御します。管理者が設定で有効にするまでオフのままで、有効になった場合にのみ、チケットの会話が設定されたプロバイダーに送信されます。
  • TICKET_RETENTION_DAYS(例: 365)を設定して、スケジュールされたスイープで、指定された期間より古い解決済みおよびクローズされたチケット(添付ファイルを含む)を永続的に削除します。

インターフェース

ワークスペースは、Slack風のナス紺色のサイドバー、セルフホストされたLatoフォント、Lucideアイコン、Radix UIプリミティブを使用しています。テーマ対応のコントロールとステータスカラーは、ライトおよびダークワークスペースをサポートします。モバイルでは、ボトムナビゲーションとキーボードアクセス可能なワークスペースドロワーにより、すべての宛先が利用可能になり、チケット列は件名の可読性を維持するように調整されます。

Cmd/Ctrl+K を使用してページ間を移動します。サイドバーのドックには、通知、テーマ切り替え、アカウントアクションが含まれています。

仕組み

ResolveHQは、自身のCloudflare Workerアカウント内で単一のCloudflare Workerとして実行されます。HonoはREST APIとReactアプリケーションの両方にサービスを提供します。Cloudflare D1はチケットと顧客を保持し、Cloudflare R2は添付ファイルを保持し、Cloudflare Queuesは送受信メールジョブを運びます。Cloudflare Email Routingは受信メールをWorkerに配信し、Resendは送信メールを送信します。チケットと添付ファイルはCloudflareアカウントに保存されます。送信メールの内容はResendを経由します。Workers Paidでは、Cloudflare Email Sendingを通じてネイティブに送信でき、メール内容はアカウントから離れません。

費用

ResolveHQは、Workers、D1、R2、Queues、Cron Triggers、Email Routingの現在の制限内で使用量が収まる限り、開始時や軽い使用量のためにCloudflareの無料プランで実行できます。無料プランを超えると予想されます。 Workers Freeはリクエストごとに10msのCPUを許可しますが、1つの無料デプロイでのサインアップは29msを測定し、他のいくつかのパスも空のワークスペースで10msを超えました。Cloudflareは、めったに制限を超えるWorkerを許容するため、そのデプロイは機能しますが、チケット、顧客、通知が蓄積するにつれてマージンは縮小します。WorkerのメトリクスでCPU時間を測定し、キューがビジーになる前にWorkers Paidに移行してください。QueuesはWorkers Freeで利用可能です。R2はアカウントのアクティベーションと請求設定が別途必要です。Resendは独自の制限内で送信メールを処理します。 無料プラン監査 を参照してください。

デプロイ

開始する最も簡単な方法は、上記の Deploy to Cloudflare ボタンを使用することです。以下が必要です:

  • Cloudflareアカウント。Workers FreeはQueuesをサポートします。R2は別途アクティベートしてください。
  • Cloudflare上のドメイン。Email Routingを設定するために必要です。
  • オプションで、送信ドメインが検証されたResendアカウント。送信メールに使用します。

サインアップする前にデプロイを確認してください。GET /api/ready はセッションなしで応答します:

curl https://<your-worker>/api/ready   # {"ok":true,"database":"ready"}

"database":"unmigrated" と報告された場合は、マイグレーションを一度適用してから再確認してください:

npx wrangler login          # このマシンがまだ認証されていない場合
npm run db:migrate:remote   # wrangler d1 migrations apply DB --remote

CloudflareダッシュボードでWorkerの Deploy command を npm run deploy に設定すると、以降のすべてのデプロイで最初にマイグレーションが実行されます。デプロイガイド を参照してください。

デプロイをエンドツーエンドで確認するには、実際のCloudflareインフラストラクチャでのみ失敗する部分も含めてください:

npm run smoke -- https://<your-worker>            # 読み取り専用チェック
npm run smoke -- https://<your-worker> --signup   # 1つの使い捨てワークスペースも作成します

その後、ResolveHQのURLを開き、オーナーとしてサインアップします。オプションでサポートメールを指定すると、それがデフォルトの受信トレイになります。Cloudflareダッシュボードで、そのアドレスをデプロイされたWorkerに送信するEmail Routingルールを追加し、テストメールを送信して受信トレイに届くことを確認してください。

デプロイフローがプロビジョニングするもの、必要な設定、初回実行セットアップ、および手動デプロイについては、デプロイガイド を参照してください。

ローカル開発

npm install
cp .dev.vars.local.example .dev.vars
npm run db:migrate:local
npm run db:seed:local
npm run dev

npm run dev は http://localhost:5173 で単一のVite開発サーバーを起動します。@cloudflare/vite-plugin は、Workersランタイム内で worker.ts を実行するため、SPAと/apiは単一のオリジンを共有し、プロキシやセカンドポートはありません。Reactアプリはホットリロードされ、Workerコードの編集はWorkerをリロードします。

npm run preview は単独では何もビルドしません。まず npm run build を実行してから、http://localhost:4173 でWorkersランタイムにビルドされた出力を提供します。これは本番環境に最も近いローカル環境です。

HTTPルートを持たないハンドラーは、ランタイムのハンドラーURLを通じてトリガーされます:

# cronハンドラーを一度実行(スケジュール自体はローカルでは発火しません)
curl "http://localhost:5173/cdn-cgi/handler/scheduled?cron=*/5+*+*+*+" 

# 生のRFC822メッセージをメールハンドラーに配信
curl -X POST --data-binary @message.eml \
  "http://localhost:5173/cdn-cgi/handler/email?from=sender@example.com&to=support@northstarlabs.test"

キューのプロデューサーとコンシューマーもローカルで実行されます。メッセージはローカルキューシミュレーターによって配信されます。Workers AIにはローカルシミュレーターがないため、ai バインディングがコメント解除されている場合、開発サーバーは wrangler login または CLOUDFLARE_API_TOKEN を必要とします。

ドキュメント

オプション設定

  • AIアシスタンス:要約、返信下書き、分類、翻訳。2つのプロバイダー:Cloudflare Workers AI(自身のカウントで実行、キー不要。wrangler.jsonc の ai ブロックをコメント解除して有効化し、オプションで WORKERS_AI_MODEL(デフォルト @cf/meta/llama-4-scout-17b-16e-instruct)または AI_GATEWAY_ID を設定してAIゲートウェイ経由で呼び出しをルーティングします。翻訳は @cf/meta/m2m100-1.2b を使用)、またはOpenAI(OPENAI_API_KEY、オプションで OPENAI_MODEL、デフォルト gpt-4o-mini)。両方が存在する場合、バインディングが優先されます。いずれの場合も、各ワークスペースは設定を通じてオプトインし、どちらも設定されていない場合は機能は非表示のままになり、AI呼び出しは行われません。ai バインディングが存在する場合、ローカル開発サーバーは wrangler login または CLOUDFLARE_API_TOKEN を必要とします。これはWorkers AIにローカルシミュレーターがないためです。
  • ネイティブ送信メール:Workers Paidでは、wrangler.jsonc の send_email バインディングをコメント解除して、ResendではなくCloudflare Email Sending経由で送信し、キューをその配信イベントにサブスクライブします。CloudflareはMessage-IDを割り当て、冪等性キーを提供しないため、確認されない送信はリトライではなく管理者のレビューのために停止します。コメントアウトしたままにすると、Resendがプロバイダーとして残ります。デプロイガイド を参照してください。
  • 保持期間:Worker変数として TICKET_RETENTION_DAYS を設定すると、指定された日数より古い解決済みおよびクローズされたチケット(添付ファイルを含む)が自動的に削除されます。未設定の場合は、何も自動削除されません。
  • Turnstile:Worker変数として TURNSTILE_SITE_KEY、Workerシークレットとして TURNSTILE_SECRET_KEY を設定すると、サインイン、サインアップ、パスワードリセットにCloudflare Turnstileチャレンジが追加されます。両方未設定のままにすると、フォームは以前とまったく同じように動作し、スクリプトはロードされず、チャレンジも表示されません。
  • 返信スレッド化トークン:Worker変数として OUTBOUND_REPLY_TOKEN=enabled を設定すると、送信メールに署名付き support+t<ticket>.<sig>@… の Reply-To アドレスが送信されます。これはResendまたはネイティブCloudflare Email Sendingのどちらでも機能します。これにより、メールシステムが識別ヘッダーを削除した場合でも返信がスレッド化されますが、カスタム support+… アドレスは正確一致ルールでは一致しないため、送信ドメインにEmail Routingのキャッチオールルールが必要です。デプロイガイド のキャッチオール要件を参照してください。

未実装

  • 多言語インターフェースとアプリ外の通知(メールダイジェスト)。
  • ワークスペースエクスポートのアプリからの復元。エクスポートは意図的に一方向です。部分的に適用される復元は、何もないより悪いからです。そのため、読み込みはデプロイガイド のドキュメント化された wrangler ランブックです。
  • MCP経由でのAIアシスタントの書き込みアクセス。アシスタントが実際の顧客に送信したいものを承認するための画面ができるまで、サーバーは読み取り専用です。

ライセンス

LICENSE を参照してください。