今週の開発者スポットライトシリーズを締めくくるのは、自身のスタートアップであるcClipをどのように構築したかを共有してくれるTejas Mehta氏です。
cClipは、実行するOSの種類にかかわらず、「コピー/ペースト」でどのデバイス間でもファイルを伝達することができる素晴らしいツールです。
cClipのとても興味深い点は、WorkersとKVをベースに構築された完全なサーバーレスアプリケーションであるにもかかわらず、独占的ではないことです。認証にはFirebase、AppleとGoogle Playストアでの統合ビューにはRevenueCat、その他のすべての請求関連作業にはStripeを使用しています。
これは、アプリケーション開発の未来を垣間見るものです。これは、現在私たちがパッケージマネージャーからパッケージをインポートするのと同じくらい簡単に他のSaaSアプリケーションを「インポート」しているであろう未来です。また、その外部アプリケーションのAPIを呼び出すことによって単方向だけではなく、Webhooksを使ってイベントを介する双方向通信も可能です。
Tejas氏の話は、次のとおりです。
cClipの原点
昨年のバーチャルな通学への突然の移行は、学校でのコミュニケーションと課題がすべてオンラインに移行することにつながりました。つまり、MacBookノートパソコンとAndroidの携帯電話を使用して微分積分の宿題を提出するには、私は各ページの写真を撮り、各写真を自分宛てにメールで送信し、コンピューターのメールにアクセスし、最後に各ファイルを提出ポータルにアップロードする必要があったのです。一度か二度、これを行うこと、それは問題ありません。でも残りの第2、3学年、これを毎日やると考えただけで、イライラしてきました。
これが、自分の携帯電話からノートパソコンに画像やリンクを送信するシンプルな転送ツールであるcClipを作るきっかけとなりました。何人かの友人がそれを見て彼らも欲しがりました。その時、多くの仮想ワークスペースに、より強力な生産性とプラットフォームにとらわれない流動性をもたらすことができると気づいたのです。
エンドツーエンド暗号化、ライブタイム更新、サブスクリプションモデルを追加した後、私はcClipを一般に公開しました。
アーキテクチャ
Erwin氏が述べたように、cClipのアーキテクチャはWorkersだけではなく、他のいくつかのSaaS製品とも統合されています。私は、Webhooksを介してモバイルアプリストアやStripeと簡単に統合させるためにRevenueCatを使用しています。Firebaseは認証とメッセージングの両方に使用され、最後に、クラウドストレージにはB2が使用されます。(少なくともR2が利用可能になるまでは)
そしてもちろん、私たちはすべての単一プラットフォーム用のクライアントも必要とします。
図で示すと、次のようになります。
クライアント
大きな課題の1つは、ありとあらゆるOSとプラットフォーム用のクライアントを作成することでした。少なくとも、iOSとAndroidの両アプリ、Windows、MacおよびWebアプリケーションも必要です。
Flutterによる助け
Flutterはクロスプラットフォームアプリケーション開発フレームワークです。これにより、UIとロジックの大部分を単一のコードベースにすることができ、さらに暗号化などの特定の部分をプラットフォーム固有のライブラリとインターフェイスできるようになりました。
FlutterはiOSとAndroidの両方を十分にサポートしていますが、Flutter Webは、まだ比較的新しいものでした。Flutter WebにはWebWorkerサポートがまだないので、暗号化などのタスクやファイルのアップロードやダウンロードにJavaScriptを多用する必要がありました。私は、より大きなファイルのメモリ消費を節約するために、ダウンロードするストリーム復号化にStreamSaver.jsを使用したかったのです。
さらにFlutter Webを使用することで、Webサイトにアクセスする代わりにcClip用のネイティブアプリが必要な場合、WindowsまたはLinuxユーザー用の電子アプリを作成することができました。
macOS
macOSには、Web 1の前に構築され、さらにそのパフォーマンスの利点のために維持されたSwiftで書かれている独自のアプリケーションがあります。macOSとiOSアプリケーションは、パスワードハッシング、ファイルのアップロードおよびダウンロード、ならびに暗号化のためのコードを共有します。
バックエンド
cClipは、Backblaze B2、Firebase認証、Firebaseデータベース、RevenueCat、Cloudflare Workersの5つの主要なバックエンドテクノロジーを使用します。Workersは、アクセス制御のコンテキスト内であろうとサブスクリプション管理であろうと、すべてのバックエンドサービス間のメインブリッジとして機能します。
認証と承認
認証フローは、Firebase認証を使用してGoogle、Appleまたはメール/パスワードでサインインしてユーザーのIDを検証し、すべての機密性の高いAPIコールにWorkersを使用することで開始されます。Workersは、正常にデコードされた時にユーザーのUIDを含むJWTであるFirebaseによって提供される「idToken」を使用して発信者のIDを検証します。ファイルのアップロード/ダウンロード、購読/購読解除などの機密性の高い操作はWorkersの後ろに置かれて、操作自体を実行する前に関連するAPIコールで「トークン」ヘッダーとして提供されるJWTを検証します。
ユーザーが認証された後、Workersは、ユーザーが特定の場所にあるファイルにアクセスできることを確認したり、RevenueCatによって通知されたときにユーザーのサブスクリプションステータスを更新したりするなど、すべてのアクセス制御を処理します。
Workersは、ユーザーのアップロードまたはダウンロード承認のGatekeeperとしての機能を果たします。リクエストが送信されると、WorkersはユーザーのIDを検証し、要求された場所とユーザーのサブスクリプションステータスを確認します。3つすべての確認が取れたら、Workersは要求されたアクションをB2で承認し、対応する情報を返します。
ストレージとメッセージ
ファイルは25MBより大きくなる可能性があり、Workers KVに保存するのは実用的ではなかったので、私はB2クラウドストレージを選択しました。B2クラウドストレージはCloudflareのBandwidth Allianceの一部であり、私がこれらのファイルに対してエグレス料金を支払うことはありません。素晴らしい特典です。
前述のように、B2の前のWorkerはアップロード、ダウンロードおよびその他のB2操作両方のすべての認証と承認を行います。
Firebaseは、すべてのクライアントとのリアルタイム通信に使用されます。例えば、サブスクリプション情報はそこに格納され、必要に応じて別のクライアントに通知を送信するのにクラウドメッセージングAPIを使用します。
サブスクリプション管理
もちろん、どのSaaSアプリケーションも大部分は請求とサブスクリプションの処理に費やされています。幸いなことに、RevenueCatは、モバイルアプリストアとStripeに関連するすべての情報を一箇所に集約してその多くを簡素化します。
つまり、3つのプロバイダーすべてからイベントを受信するためにWebhooksのセットを1つ実装する必要があっただけで、受信したイベントはすべて同じ形式に従っていました。
ほとんどの発信サブスクリプションのAPIコールもRevenueCatを経由しますが、StripeとGoogle Playストアに直接接続する必要があるエッジケースがいくつかあります。
最大の課題
クロスプラットフォームサブスクリプション
クロスプラットフォームサブスクリプション、またはすべてのプラットフォームでのユーザーのサブスクリプションの同期は、大きな課題でした。サブスクリプションが二重に発生しないようにすることから、ステータスのライブタイムの変更まで、すべてが完全に統合されなければなりませんでした。RevenueCatはプロセスを大幅に簡素化しましたが、まだクロスプラットフォームのキャンセルと更新を管理する必要がありました。私はFirebaseデータベースにステータスを保存し、最後にユーザー側で更新を待機させることによってこれを管理しました。この情報は書き込みロックされており、いかなるステータスの偽装も防止するために検証されたWorkerのみにステータスの更新を許可しました。
ファイルの容量制限
もう1つの難しい課題は、ファイルストレージのトラッキングでした。最初の計画において、私はB2のAPIがディレクトリのストレージ合計のゲッターをサポートしないことが分かり、「b2_list_file_names」APIで返されるすべてのファイル値を合計する必要がありました。APIは最も高価なオペレーションの1つとして分類されるので、これは時間と費用の面でコストがかかりました。それに加えて、ユーザーがファイルをアップロードまたは削除するたびにAPIが呼び出されることになります。その結果、異なるファイルの変更(アップロードまたは削除)でB2と同期するカウンターを作成し、それはWorkerによってFirebaseのデータベースに格納され、FirebaseのSDK変更リスナーを介してユーザーのデバイス上でリアルタイム更新ができるようにしました。
しかし、私はユーザーがファイルに使用できるのに十分なストレージを所有していることを検証するために、ユーザーは使用済みストレージ数を変更できないようにし、すべてのアップロード操作を有効にするようにしたかったのです。アプリはWorkersと最初に対話し、ユーザーが十分なストレージを持っていることを確認すると、アップロードの承認が返されます。
総合的な体験
Workers & KVは、コストのかかるAPIコールを節約し、cClipの完全なサーバーレスバックエンドを提供するためのコアソリューションとして機能しました。プラットフォームの簡単な性質に加えて、テストの変更は非常に簡単で、APIの最新の変更をテストするためのクイックプレビューまたはステージング環境が必要でした。
Node.js StripeはまだWorkersに対応していませんが、問題なく動作したフェッチで直接Stripe APIエンドポイントを使用したため、私のWorkers最大の課題は、Stripeライブラリの欠如でした。しかし、先ほどStripeがWorkersと互換性のあるSDKを立ち上げたと知りました。素晴らしい!
私は現在、デバイス間でどんなファイルでも共有できるようにするピアツーピア転送プロトコルであるcClip Direct実装の刷新に取り組んでいます。Workersを使用して通知とシグナリングを管理し、最初のハンドシェイクプロセスが修正され、FirebaseからDurable ObjectsとWebSocketにリアルタイム通信の移動を検討しています。そしてもちろん、R2が利用可能になった時に試すのが待ちきれません。
何であれ、自分が必要として作ったアプリケーションが特に問題なく本格的な製品へと拡張できたのを見れて、本当に良かったです。
今週の開発者スポットライトシリーズにお付き合いいただき、ありがとうございます。私たちのお客様のCloudflareエコシステムを使用した興味深い問題の解決方法について、楽しんでお読み頂けたら幸いです。今後さらにスポットライトを行っていきたいので、もっと読みたい場合や、私たちに書いて欲しいユースケースがある場合は、お気軽にTwitterや以下リンクのディスカッションで私までお知らせください。