Skip to content

データモデル

newland-core は Cloudflare D1(SQLite)と Cloudflare KV を使用します。

D1 テーブル定義(USER_DB)

tenants

マルチテナントの組織情報を管理するテーブル。

カラム制約説明
idTEXTPKテナント ID(UUID)
slugTEXTUNIQUE NOT NULLURL フレンドリーな識別子(例: newland-studio
display_nameTEXTNOT NULL表示名
is_activeINTEGERNOT NULL DEFAULT 1有効フラグ(1=有効, 0=無効)
created_atINTEGERNOT NULLUnix タイムスタンプ (ms)

インデックス: idx_tenants_slug (slug)


users

テナントに紐づくユーザー情報。

カラム制約説明
idTEXTPKユーザー ID(UUID)
tenant_idTEXTFK → tenants.id所属テナント
emailTEXTUNIQUE (tenant_id, email)メールアドレス(テナント内ユニーク)
password_hashTEXTNOT NULLbcrypt ハッシュ
display_nameTEXTNULL表示名
roleTEXTNOT NULL DEFAULT 'member'ロール(owner / admin / member
is_activeINTEGERNOT NULL DEFAULT 1有効フラグ
created_atINTEGERNOT NULLUnix タイムスタンプ (ms)
updated_atINTEGERNOT NULLUnix タイムスタンプ (ms)
last_login_atINTEGERNULL最終ログイン日時

インデックス:

  • idx_users_tenant_email (tenant_id, email)
  • idx_users_tenant_id (tenant_id)

refresh_tokens

発行済みリフレッシュトークンの管理。

カラム制約説明
idTEXTPKトークン ID
user_idTEXTFK → users.id発行ユーザー
tenant_idTEXTNOT NULLテナント ID
token_hashTEXTNOT NULLトークンのハッシュ値
issued_atINTEGERNOT NULL発行日時 (Unix ms)
expires_atINTEGERNOT NULL有効期限 (Unix ms)
revoked_atINTEGERNULL無効化日時(NULL=有効)
user_agentTEXTNULLクライアント User-Agent
ip_addressTEXTNULLクライアント IP

インデックス:

  • idx_refresh_tokens_user_tenant (user_id, tenant_id)
  • idx_refresh_tokens_token_hash (token_hash)

KV スキーマ(SESSION_KV)

キーパターン値(JSON)TTL用途
session:{jti}SessionPayload3600秒アクセストークン有効セッション
denylist:{jti}"1"トークン残存期間ログアウト済みトークンの拒否リスト
tenant:slug:{slug}{ id: string, isActive: boolean }3600秒テナント解決キャッシュ

SessionPayload 型

typescript
interface SessionPayload {
  userId:    string
  tenantId:  string
  role:      'owner' | 'admin' | 'member'
  email:     string
  jti:       string
  expiresAt: number
}

KV 操作フロー

ログイン:
  WRITE  session:{jti}       = SessionPayload  (TTL: 3600s)

ログアウト:
  DELETE session:{jti}
  WRITE  denylist:{jti}      = "1"             (TTL: 残存秒数)

トークン検証:
  READ   denylist:{jti}      → 存在すれば revoked
  READ   session:{jti}       → null なら expired

テナント解決:
  READ   tenant:slug:{slug}  → miss なら D1 から取得して WRITE

Service Binding (RPC)

他の Cloudflare Worker から newland-core をゼロコピー RPC で呼び出せます。

wrangler.toml 設定(呼び出し元 Worker)

toml
[[services]]
binding = "CORE_RPC"
service = "newland-core"
entrypoint = "CoreRpc"

使用例

typescript
interface Env {
  CORE_RPC: Service<CoreRpc>
}

// トークン検証
const result = await env.CORE_RPC.verifyToken({ token: accessToken })
if (!result.valid) {
  return c.json({ error: result.reason }, 401)
}
// result.userId, result.tenantId, result.role, result.email が利用可能

// ユーザープロフィール取得
const user = await env.CORE_RPC.getUserProfile(userId, tenantId)

CoreRpc メソッド一覧

メソッド引数戻り値説明
verifyToken(req){ token: string }VerifyTokenResponseJWT 検証 + セッション確認
getUserProfile(userId, tenantId)string, stringUser | nullD1 からユーザー取得
typescript
type VerifyTokenResponse =
  | { valid: true;  userId: string; tenantId: string; role: 'owner' | 'admin' | 'member'; email: string }
  | { valid: false; reason: 'expired' | 'revoked' | 'malformed' | 'tenant_mismatch' }

newland-studio/newland-core