0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

# Cloudflare Workers + D1 + R2 + Supabase Auth で月額 $0 のオープンデータ管理システムを作った

0
Posted at

以下AI生成の文章なので、読みにくく申し訳ありません。

自治体向けのオープンデータ管理システムを OSS として公開した。

「なぜ作ったか」という話は note に書いた → https://note.com/easy_deer7322/n/nfc62903bb2b2?app_launch=false

この記事では技術的な話に絞る。


作ったもの

CSVなどのファイルを管理画面からアップロードして公開するシステム。D1に管理データを持ち、Sync Worker が定期的に R2 へ JSON を生成、公開APIはその R2 をカスタムドメインで直接配信する構成。

リポジトリ: https://github.com/pons-llc/opendata_platform

アーキテクチャ

公開API (opendata.example.lg.jp)
  └── R2カスタムドメイン直接配信 (Workerなし)
      └── datasets/index.json, datasets/{id}.json, etc.

公開ポータル (任意)
  └── Public Worker → R2 (JSON 読み取りのみ、D1不使用)

管理画面 (admin.example.lg.jp)
  └── Admin Worker → D1 (CRUD) + R2 (ファイルアップロード)
      認証: Supabase Auth (Cookie + JWT / ES256)

アップロードAPI (admin.example.lg.jp/api/upload/*)
  └── Admin Worker → D1 (APIキー検証) + R2 (ファイル保存)
      認証: データセットごとのAPIキー (Bearer)

Sync Worker (Cloudflare Cron)
  └── D1 (公開中データセット) → R2 (JSON生成)
      日次で R2 孤立ファイルのクリーンアップも実行

公開APIはWorkerを経由しない。R2のカスタムドメインで静的JSONを直接配信するだけなので、アクセスが増えてもWorkerの枠を消費しない。これがコストを下げる最大のポイント。


技術スタック

レイヤー 技術 用途
ランタイム Cloudflare Workers 管理API・Sync処理
DB Cloudflare D1 (SQLite) データセット・ユーザー・部署管理
ストレージ Cloudflare R2 ファイル・公開JSON
認証 Supabase Auth JWT発行・セッション管理
フロントエンド Vanilla JS + MPA サーバーサイドHTMLレンダリング

フロントエンドはバンドラーなし。Workers でHTMLを直接レンダリングする MPA 構成。SPAにする理由がないので、シンプルさを優先した。


なぜこのスタックか:コスト試算

Cloudflare 無料枠

サービス 無料枠 このシステムの消費量目安
Workers 10万req/日 管理画面で数百req/日 → 余裕
D1 読み取り 2,500万行/日 問題なし
D1 書き込み 5万行/日 問題なし
R2 ストレージ 10GB/月 数GB程度
R2 Class A (write) 100万ops/月 15分sync × 96回 × 30日 × ~60ファイル ≒ 17万ops
R2 Class B (read) 1,000万ops/月 公開API直接配信のためWorker不使用

管理画面のリクエストはたかが知れている。Sync はCronで15分おきに動くが、1回あたりのR2書き込みはデータセット数と同程度。60件なら17万ops/月で余裕。

Supabase 無料枠

認証にしか使っていないので、MAU 5万・DB 500MB の Free プランで十分。職員が数十人なら問題ない。

結論: 小〜中規模自治体であれば月額 $0 で動く。


認証設計

Supabase Auth の JWT を Workers 側で検証する構成。

// ES256 (ECC P-256) で検証
// Supabase は HS256 か ES256 を選べる。本番は ES256 推奨
async function verifyJWT(token, secret) {
  // ...
}

セッションは HTTP-Only Cookie に保持(XSS対策)。JWT の有効期限は1日に設定し、ページリクエストのたびに Supabase の /auth/v1/user でユーザー情報を取得して有効性を確認している。

ローカル開発時は DEV_MODE=true/dev-login から任意のユーザーでログインできるが、本番環境では無効化される。


ロール設計

ロール 権限
admin 全データセット編集・ユーザー管理・部署管理・カテゴリー管理
editor 所属部署のデータセットのみ編集可(複数部署対応)

editor は user_departments テーブルで複数部署に所属できる。APIは部署チェックを行い、他部署のデータは読み取り専用。


APIキー設計(外部連携用)

kintone など外部システムからCSVを自動アップロードするためのAPIキー機能を実装した。

  • データセットごとにAPIキーを発行
  • DB には SHA-256 ハッシュのみ保存(平文は発行時のみ表示)
  • キーの先頭12文字をプレフィックスとして保存し、管理画面での識別に使う
  • last_used_at を記録して利用状況を確認できる
curl -X POST \
  -H "Authorization: Bearer odk_xxxxxxxxxxxxxxxx" \
  -F "file=@data.csv" \
  https://admin.example.lg.jp/api/upload/{datasetId}

Sync Worker の仕組み

Admin Worker がデータを変更しても、公開APIには即時反映されない。Sync Worker が Cron で定期実行して D1 の内容を R2 に JSON として書き出す構成。

D1 (公開中データセット) →  Sync Worker → R2
                                 ├── metadata.json
                                 ├── datasets/index.json
                                 ├── datasets/{id}.json
                                 ├── departments/index.json
                                 ├── departments/{slug}/datasets.json
                                 ├── categories/index.json
                                 └── categories/{slug}/datasets.json

Cron スケジュール:

  • 本番: ["*/15 * * * *", "0 3 * * *"] — 15分ごとにsync、深夜3時にsync+cleanup
  • 開発時: ["0 3 * * *"] — R2のClass A操作($4.50/M ops)を節約するため日次1本

R2 の孤立ファイルクリーンアップ

ファイルをアップロードしてリソースを削除しても、R2上のオブジェクトは残る。日次バッチで D1 に存在しないR2オブジェクトを削除するクリーンアップを実装している。

D1に存在するファイルパスの集合 と R2のオブジェクト一覧を突合して、差分を削除するシンプルな実装。


セキュリティ設計のまとめ

  • IP制限: Cloudflare WAF カスタムルールで管理画面ドメインを制限(無料枠5ルール以内)
  • 認証: Supabase Auth JWT (ES256) + HTTP-Only Cookie
  • ロール: admin/editor の2段階、editorは所属部署のみ
  • APIキー: SHA-256ハッシュのみDB保存、平文は発行時のみ
  • R2分離: 公開APIはWorkerなしR2直接配信 → D1に一切触れない
  • CORS: R2バケットのCORSポリシーで制御

デプロイ

README にステップバイステップのチェックリストを書いた。大まかな流れ:

  1. Supabase プロジェクト作成
  2. wrangler.sample.jsoncwrangler.jsonc にコピーして設定
  3. npx wrangler d1 createnpx wrangler r2 bucket create
  4. npm run migrate で D1 マイグレーション
  5. npm run deploy でデプロイ
  6. Cloudflare Dashboard で Secrets 設定(API キーなどはここで設定)
  7. R2 カスタムドメイン設定
  8. 管理者ユーザーを Supabase で招待して D1 で admin に昇格

今後やりたいこと

  • データプレビュー(CSVをテーブル表示)
  • データセットの公開スケジュール設定
  • 変更履歴・監査ログ

リポジトリ: https://github.com/pons-llc/opendata_platform

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?