Omnigraph
Graph engine for context assembly. Object-storage native. Agents work with graph data the way engineers work with code: branched, typed, reviewed, merged, audited, and governed.
官民共存型オープンデータ管理システム。行政・民間が双方向にデータを持ち寄り、住所などの表記ゆれを自動で名寄せ・マージし、カタログと REST API で利活用できる基盤です。
Translation pending
DataTypeScript · JavaScriptGitHub @tottorigeekOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
官民共存型オープンデータ管理システム — 表記ゆれ正規化つきマージエンジンを備えたオープンデータ基盤 (AGPL-3.0)
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗官民共存型オープンデータ管理システム — 行政と民間が双方向にデータを持ち寄り、 住所などの表記ゆれを自動で名寄せ・マージして、公開カタログと REST API で利活用するための基盤です。
既存の CKAN / Socrata / OpenDataSoft などが「行政が一方向に公開する」モデルであるのに対し、 OpenData Bridge は 官民双方向の持ち寄り + 表記ゆれ正規化つきマージエンジン を差別化の核としています。
公開インスタンス: **https://opendata-bridge.vercel.app**(サービス紹介は /about)
| 機能 | 概要 |
|---|---|
| 組織・認証 | 組織(行政 / 民間)単位のアカウント。セッションは署名付き JWT(HttpOnly Cookie)。 |
| データセット管理 | CSV アップロード、メタデータ(タイトル・説明・ライセンス・タグ・更新頻度・公開範囲)管理。 |
| 外部データソース連携 | 外部の REST API(JSON)や CSV の URL をデータセットに関連付け、手動 / 定期(毎日)で取り込み。取得結果は CSV アップロードと同じ実体に書き込まれるため、カタログ・マージ・公開 API がそのまま使える。 |
| 承認フロー | 下書き → 承認待ち → 公開 / 差し戻し。公開は組織の ADMIN のみ承認可能。 |
| 公開カタログ | 公開データセットをキーワード・組織種別・タグ・自治体(都道府県 / 市区町村) で横断検索。CSV ダウンロード対応。 |
| 地域メタデータ | データセットに「対象地域」、組織に「所在地」を設定。対象地域が未設定のデータセットは組織の所在地で絞り込まれる。市区町村は総務省の全国地方公共団体コード(47 都道府県 / 1,741 市区町村)に基づくマスタから選択。 |
| マージエンジン | 2 つのデータセットをキー列 + 正規化レベルで名寄せ結合。マージ型は 項目拡張型(片方の行をすべて残して列を足す)と 共通抽出型(両方にある行だけ残す)の 2 種。住所・カナ・電話・日付などの表記ゆれを吸収し、双方向のカバー率などの指標とアンマッチ例を提示。結果は出典(来歴)と版を保持した新データセットとして保存し、元データの更新に追従できる。詳細は docs/design/merge-design.md。 |
| REST API v1 | API キー(Bearer)認証で公開データセットの一覧・メタデータ・本体(JSON / CSV)を取得。呼び出し量を自動計測。 |
別システムのデータベースが公開している API を、データセットに 関連付けて随時引き込む 仕組みです。CSV アップロードを置き換えるものではなく、同じ器を埋める 2 つ目の経路として 共存します。
CSV アップロード ─┐
├─→ storage の datasets/{id}.csv ─→ カタログ / ダウンロード / マージ / REST API v1
外部 API 取り込み ─┘
取得したデータは既存の CSV ストレージ(lib/storage.ts)へ書き出されます。この一点に
揃えているため、下流の機能は一切変更していません。API 取り込みのデータセットも、
そのままマージエンジンの入力にでき、公開カタログにも載り、REST API v1 からも読めます。
| 項目 | 対応内容 |
|---|---|
| 取得方式 | REST API(JSON)/ CSV の URL |
| 認証 | なし / Bearer トークン / 任意のヘッダ / クエリパラメータ |
| レコード位置 | ドット区切りで指定(CKAN なら result.records)。直下が配列なら省略可 |
| 列マッピング | 取得元のキー → 保存する列名。未指定なら全キーを自動で列にする |
| 同期 | 手動(いつでも)/ 定期(毎日・syncMode = SCHEDULED) |
| 接続テスト | 保存前に取得を試し、列と先頭 10 行を確認できる |
| 履歴 | 成功 / 失敗・行数・所要時間・実行種別を SyncRun に記録 |
設定はデータセット詳細画面 /dashboard/datasets/[id] の「外部データソース」パネルから行います。
定期同期の有効化: CRON_SECRET を環境変数に設定してください(未設定なら定期同期は
503 で無効。手動同期は使えます)。スケジュールは vercel.json の
0 18 * * *(UTC)= 毎日 03:00 JST です。
取り込みの設計上の判断:
lib/sources/fetch.ts)。
クラウドのメタデータ(169.254.169.254)や内部ホストへは到達できません。lib/crypto.ts)。相手に提示する
必要があるためハッシュ化はできませんが、平文では保存しません。画面にも再表示されません。/ — ランディングページ/catalog — 公開データカタログ(検索・フィルタ) / /catalog/[id] — データセット詳細・ダウンロード/signup /login — 組織登録 / ログイン/dashboard — ダッシュボード
/dashboard/datasets — 自組織のデータセット一覧 / new・[id](外部データソース設定を含む)・[id]/edit/dashboard/merge — マージウィザード/dashboard/api-keys — API キー発行・失効・利用量/dashboard/settings — 組織設定DATABASE_URL と直接続の DIRECT_URL を使用)storage/datasets/)/ Supabase Storage(非公開バケット)を環境変数で自動切替(lib/storage.ts)jose(JWT)+ bcryptjscsv-parse / csv-stringify前提:
# 1. 取得
git clone <this-repo>
cd opendata-manage-system
# 2. 依存インストール(postinstall で prisma generate が走ります)
npm ci
# 3. 環境変数を用意
cp .env.example .env
# .env を編集し、以下を設定してください。
# - DATABASE_URL: PostgreSQL の接続文字列(Supabase のプーラー 6543 + ?pgbouncer=true、
# またはローカル PostgreSQL)
# - DIRECT_URL: マイグレーション用の直接続(Supabase の 5432。ローカル PostgreSQL の
# 場合は DATABASE_URL と同じ値でも可)
# - SESSION_SECRET: 十分長いランダム値(例: openssl rand -base64 32)
# SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY は未設定でOK
# (未設定時は CSV をローカルの storage/datasets/ に保存)
# 4. DB マイグレーション(PostgreSQL に接続してテーブル作成)
npx prisma migrate deploy # prisma/migrations の初期マイグレーションを適用
# (スキーマを変更して作り直す場合は: npx prisma migrate dev)
# 5. シードデータ投入(組織2・ユーザー2・公開データセット4件)
npx prisma db seed
# 6. 開発サーバー
npm run dev # http://localhost:3000
ストレージについて:
SUPABASE_URLとSUPABASE_SERVICE_ROLE_KEYが両方設定されていれば CSV は Supabase Storage(非公開バケット)へ、未設定ならローカルのstorage/datasets/へ 保存されます。どちらを使うかはlib/storage.tsが環境変数だけで自動判定します。
シード投入後、以下のアカウントでログインできます(パスワードは共通で demo1234)。
| メールアドレス | 組織 | 種別 | 権限 |
|---|---|---|---|
[email protected] | 鳥取県庁 | 行政 | ADMIN |
[email protected] | 山陰データラボ株式会社 | 民間 | ADMIN |
投入される公開データセット(すべて PUBLISHED / 一般公開):
1 と 3 は住所が一部同一地点を指しており、マージウィザードで キー列「住所」/ 正規化レベル「住所 (address)」で結合すると 表記ゆれを吸収して 約 40% のマッチ率 で名寄せできます(マージ機能のデモ)。
シードは冪等です。npx prisma db seed を何度実行しても、既存の組織・ユーザー・
データセットと CSV を作り直し、常に上記 4 件の状態になります。
元 CSV は prisma/seed-data/ にコミットされており、実行時に lib/storage.ts 経由で
保存されます(ローカルは storage/datasets/、Supabase 設定時は Supabase Storage の非公開バケット)。
API キーは /dashboard/api-keys で発行します。認証は Bearer 方式です。
curl -H "Authorization: Bearer odb_あなたのキー" \
"http://localhost:3000/api/v1/datasets?q=避難所&org_type=GOVERNMENT"
エンドポイント・認可・エラー形式の詳細は docs/api.md を参照してください。
DB は Supabase Postgres、CSV ファイルは Supabase Storage(非公開バケット) を使います。
ホスティングは Vercel です。Vercel の Marketplace Integration(Supabase) で接続すると、
DB 接続用の環境変数が 自動注入 され、手動で設定するのは SESSION_SECRET だけで済みます。
従来どおり
DATABASE_URL/DIRECT_URLなどを手動設定する方法も引き続き使えます (「B. 手動で環境変数を設定する場合」を参照)。アプリは注入変数・手動変数のどちらでも動くようlib/db-env.tsで接続 URL を解決します。
Vercel ダッシュボードで Add New… → Project を開き、GitHub の
tottorigeek/opendata-bridge を Import します(Framework は Next.js が自動検出)。
プロジェクトの Storage(または Integrations)タブ → Marketplace から Supabase を選び、 新規 Supabase プロジェクトを作成(リージョンは Tokyo 推奨)、または既存プロジェクトを接続します。 接続すると、以下の環境変数が Production / Preview に自動注入されます (出典: Supabase 公式ドキュメント):
POSTGRES_PRISMA_URL … プーラー(Prisma 用)接続文字列 → 本アプリの通常クエリ用 DATABASE_URL として利用POSTGRES_URL_NON_POOLING … 直接続 → マイグレーション用 DIRECT_URL として利用POSTGRES_URL / SUPABASE_URL / SUPABASE_JWT_SECRET ほかSQL を手で流す必要はありません。 テーブルはデプロイ時のビルドコマンドが
prisma/migrationsの初期マイグレーションから自動作成します。Marketplace 版のプーラー URL には
pgbouncer=trueが付かない 既知の問題がありますが、 本アプリはプーラー URL(ポート 6543 /pooler.supabase.com)を検出して自動付与するため 追加設定は不要です。
Supabase ダッシュボード → Storage → New bucket で、名前 datasets のバケットを作成します。
Public バケットにはしない(非公開のまま)でください。CSV は必ずアプリのルート経由で配信します。
別名にする場合は、環境変数 SUPABASE_STORAGE_BUCKET にその名前を設定します。
Storage を使うには
SUPABASE_SERVICE_ROLE_KEYが必要です。 Marketplace Integration は service_role キーを この名前では注入しません(公式にはSUPABASE_SECRET_KEY等を注入)。 CSV を Supabase Storage に保存する場合は、Supabase の Project Settings → API の service_role キーを、Vercel の環境変数にSUPABASE_SERVICE_ROLE_KEYという名前で 手動追加してください(未設定ならローカルstorage/datasets/動作にフォールバックします)。
SESSION_SECRET を手動追加Vercel の Settings → Environment Variables(Production / Preview 両方)に、
SESSION_SECRET を追加します(値は openssl rand -base64 32 で生成)。
Integration を使う場合、手動追加が必須なのは基本これだけです
(Storage を使うなら上記 SUPABASE_SERVICE_ROLE_KEY も)。
Deploy を実行します。ビルドコマンド(package.json の build =
node scripts/migrate-deploy.mjs && next build)が、注入変数・手動変数のどちらからでも
接続 URL を解決し、ビルド時に prisma/migrations の初期マイグレーションを
直接続(DIRECT_URL / POSTGRES_URL_NON_POOLING)経由で適用します。
以降は GitHub への push で自動再デプロイされます。
関数のリージョンは DB と同じ地域にしてください。 vercel.json の
regionsでhnd1(東京)を指定しています。Vercel の既定は米国東部(iad1)で、 東京の Supabase と組み合わせると DB への問い合わせのたびに太平洋を往復し、 DB を使うページの表示が 1〜2 秒遅くなります。Supabase を別リージョンに作った場合は、regionsをそのリージョンに近い値(一覧)に変えてください。
シードは Vercel 上では自動実行されません。本番の環境変数をローカルに取得して一度だけ実行します。
Vercel CLI の vercel env pull を使うと、Integration が注入した変数も
まとめて取得できます。
vercel link # 対象プロジェクトに紐付け(初回のみ)
vercel env pull .env.production # 注入変数を含む環境変数を取得
# Storage にもシード CSV を入れる場合は、上記で service role キーが取得できていなければ
# .env.production に SUPABASE_SERVICE_ROLE_KEY を追記しておく
set -a; . ./.env.production; set +a
npx prisma db seed # scripts/seed.mjs 経由で接続 URL を解決して実行
SUPABASE_URL(またはNEXT_PUBLIC_SUPABASE_URL)とSUPABASE_SERVICE_ROLE_KEYが 揃っているとシードの CSV も Supabase Storage の非公開バケットに保存されます。揃っていないと DB だけが投入され CSV 本文が Storage に入らない(=ダウンロードできない)ため注意してください。
Integration を使わず、Settings → Environment Variables(Production / Preview 両方)に自分で設定する
従来の方法も使えます。この場合は次を設定します(Storage を使わないなら下 2 つは省略可)。
| 変数 | 取得場所 |
|---|---|
DATABASE_URL | Supabase の Connect(画面上部)→ ORMs / Prisma の Transaction pooler(ポート 6543)。末尾に ?pgbouncer=true を付ける。 |
DIRECT_URL | Supabase の Connect → Session pooler(pooler.supabase.com のポート 5432)。※ Direct connection(db.<ref>.supabase.co)は IPv6 専用のため、Vercel のビルド環境からは到達できません。 |
SESSION_SECRET | 手元で生成: openssl rand -base64 32。 |
SUPABASE_URL | Project Settings → API の Project URL(https://<project-ref>.supabase.co)。Storage 利用時のみ。 |
SUPABASE_SERVICE_ROLE_KEY | Project Settings → API の service_role キー(サーバー専用シークレット)。Storage 利用時のみ。 |
既にほかのアプリが使っている Supabase プロジェクトに相乗りする場合、public スキーマが空でないため
prisma migrate deploy が P3005(The database schema is not empty) で失敗します。
その場合は本アプリ専用の PostgreSQL スキーマに分離します(テーブル名の衝突も完全に回避できます)。
CREATE SCHEMA IF NOT EXISTS opendata;DATABASE_URL … ...?pgbouncer=true&schema=opendataDIRECT_URL … ...?schema=opendataテーブル(_prisma_migrations 含む)はすべて opendata スキーマ内に作成され、public には触れません。
Supabase のテーブルエディタではスキーマ切替で opendata を選ぶと確認できます。
| 対策 | 内容 |
|---|---|
| API キー | DB には SHA-256 ハッシュのみ保存(ApiKey.keyHash)。全文は発行時レスポンスでしか返さず以降は復元不可。一覧表示は keyPrefix(先頭 8 文字)から組み立てる。 |
| セッション失効 | JWT に jti を持たせ Session テーブルで台帳管理。ログアウトで revokedAt を立てるため、トークンが漏れても即座に無効化できる(revokeAllUserSessions() で全端末失効)。 |
| レート制限 | RateLimit テーブルによる固定ウィンドウ方式(サーバーレスではインスタンス間でメモリを共有できないため DB を集計点にする)。ログイン 10回/5分(+ IP 30回/5分)、サインアップ 5回/時、公開 API 600回/分、マージ実行 20回/5分。 |
| ユーザー列挙対策 | ログイン失敗時、ユーザー不在でもダミーハッシュに対して bcrypt を実行し、応答時間の差から登録有無が漏れないようにする。 |
| CSV インジェクション | ダウンロード・API の CSV 出力で、= + - @ 等で始まるセルにシングルクォートを前置(-1.5 のような数値は除外)。他組織が Excel で開く前提のため必須。 |
| マージの DoS 対策 | 入力 20 万行 / 出力 50 万行の上限。多対多結合による行数爆発を結合ループ内で検知して 413 を返す。 |
| 組織種別の詐称対策 | 種別は登録時の自己申告のため、Organization.verified が false の間はカタログで「行政(未確認)」と中立表示する。 |
| セキュリティヘッダ | next.config.ts で CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy / HSTS(本番のみ)を全レスポンスに付与。X-Powered-By は無効化。 |
組織に verified を付与する方法: 現状は管理 UI が無いため、Supabase の SQL Editor で
UPDATE "Organization" SET "verified" = true WHERE id = '<組織ID>';を実行します。 公式ドメインのメールや公文書などで実在を確認してから付与してください。
マイグレーションの注意:
0002_security_hardeningは既存の平文 API キーを PostgreSQL のsha256()でその場ハッシュ化するため、発行済みキーは失効しません。sha256()は PostgreSQL 11 以降が必要です(Supabase は対応済み)。
lib/storage.ts が service_role キーで認証したサーバー SDK 経由でのみ本文を読み書きします。
公開 URL・署名 URL は使いません。ダウンロードは /api/datasets/[id]/download が
組織 / 公開設定に応じて認可した上で本文をストリーム配信します。SUPABASE_SERVICE_ROLE_KEY はサーバー専用シークレットです。クライアント側コードや
NEXT_PUBLIC_* 変数には絶対に置かないでください。SESSION_SECRET は既定値のまま運用しないでください(Cookie 署名鍵)。NODE_ENV=production で配信されるため、セッション Cookie の secure 属性が有効になります。GNU Affero General Public License v3.0(AGPL-3.0) で公開しています。全文は LICENSE を参照してください。
同梱データ(市区町村マスタ)の出典は NOTICE に記載しています。
脆弱性の報告は SECURITY.md、コントリビューションについては CONTRIBUTING.md を参照してください。