テナント削除

テナントとそれに関連するすべてのデータを完全に削除します。この操作は安全のために3重の検証が必要です。

重要: このページのすべてのエンドポイントはグローバルAPIキーが必要です。これは取り消しできない危険な操作です。


テナント削除

DELETE /api/tenant

テナントとそれに関連するすべてのデータを完全に削除します。安全のため3重の検証が必要です。

警告

この操作は元に戻せません。すべてのテナントデータが完全に削除されます:

  • すべてのプロジェクトとそのデータセット
  • すべての調査、ノートブック、ダッシュボード
  • BLOBストレージコンテナとファイル
  • データベースのレコードおよび設定
  • テナントへのユーザー割り当て(ユーザー自体は削除されません)

テナントを削除する前に、重要なデータを必ずエクスポートしてください。


リクエストボディ

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation"
}

3重検証

削除を進めるには、3つの識別子すべてが正確に一致している必要があります:

フィールド 種類 必須 説明
tenantId GUID 必須 削除対象のテナントID
name 文字列 必須 テナント名(正確に一致する必要があります)
displayName 文字列 必須 表示名(正確に一致する必要があります)

この3重検証によって、3つの識別子すべてを知り確認する必要があり、誤削除を防止します。


レスポンス(200 OK)

{
  "message": "Tenant 'Acme Corporation' deleted successfully",
  "tenantName": "acme-corp",
  "tenantDisplayName": "Acme Corporation",
  "storageContainerDeleted": true
}

レスポンスフィールド

フィールド 種類 説明
message 文字列 削除成功の確認メッセージ
tenantName 文字列 削除されたテナントのシステム名
tenantDisplayName 文字列 削除されたテナントの表示名
storageContainerDeleted ブール Blobストレージが削除されたか

エラー応答

Not Found (404)

{
  "error": "Tenant not found with ID '12345678-1234-1234-1234-123456789012'"
}

検証失敗 (400)

テナント名が不一致の場合:

{
  "error": "Tenant name 'wrong-name' does not match the tenant with ID '12345678-1234-1234-1234-123456789012'. Expected 'acme-corp'.",
  "hint": "安全のために3つの識別子(ID、名前、表示名)がすべて正確に一致する必要があります"
}

表示名が不一致の場合:

{
  "error": "Display name 'Wrong Name' does not match the tenant with ID '12345678-1234-1234-1234-123456789012'. Expected 'Acme Corporation'.",
  "hint": "安全のために3つの識別子(ID、名前、表示名)がすべて正確に一致する必要があります"
}

認証エラー (401)

{
  "error": "This endpoint requires a Global API key.",
  "hint": "グローバルAPIキーは /admin/global-api-keys で作成できます"
}

実装例

cURL

# テナント削除(3重検証が必要)
curl -X DELETE "https://your-mindzie-instance.com/api/tenant" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "12345678-1234-1234-1234-123456789012",
    "name": "test-tenant",
    "displayName": "Test Tenant"
  }'

Python

import requests

BASE_URL = 'https://your-mindzie-instance.com'

class TenantDeleter:
    def __init__(self, global_api_key):
        """グローバルAPIキー(テナント固有ではない)で初期化します。"""
        self.headers = {
            'Authorization': f'Bearer {global_api_key}',
            'Content-Type': 'application/json'
        }

    def get_tenant(self, tenant_id):
        """検証用にテナント詳細を取得します。"""
        url = f'{BASE_URL}/api/tenant/{tenant_id}'
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def delete_tenant(self, tenant_id, name, display_name, confirm=False):
        """
        3重検証によるテナント削除。

        引数:
            tenant_id: テナントのGUID
            name: テナントのシステム名(正確に一致)
            display_name: テナントの表示名(正確に一致)
            confirm: Trueに設定すると実際に削除を実行
        """
        if not confirm:
            # 検証モード - 値が一致するか確認
            tenant = self.get_tenant(tenant_id)

            errors = []
            if tenant['name'] != name:
                errors.append(f"名前の不一致: 期待値 '{tenant['name']}', 入力値 '{name}'")
            if tenant['displayName'] != display_name:
                errors.append(f"表示名の不一致: 期待値 '{tenant['displayName']}', 入力値 '{display_name}'")

            if errors:
                raise ValueError("検証失敗:\n" + "\n".join(errors))

            print(f"テナント '{display_name}' ({name}) の検証に成功しました")
            print(f"  - ID: {tenant_id}")
            print(f"  - ユーザー数: {tenant.get('userCount', 'N/A')}")
            print(f"  - ケース数: {tenant.get('caseCount', 'N/A')}")
            print("\n削除を実行するには confirm=True を指定して呼び出してください。")
            return None

        # 削除を実行
        url = f'{BASE_URL}/api/tenant'
        payload = {
            'tenantId': tenant_id,
            'name': name,
            'displayName': display_name
        }

        response = requests.delete(url, json=payload, headers=self.headers)

        if response.ok:
            return response.json()
        elif response.status_code == 404:
            raise Exception(f'テナントが見つかりません: {tenant_id}')
        elif response.status_code == 400:
            error = response.json()
            raise Exception(f"検証失敗: {error.get('error', '不明なエラー')}")
        else:
            raise Exception(f'テナント削除に失敗しました: {response.text}')

# 使用例 - 安全な削除ワークフロー
deleter = TenantDeleter('your-global-api-key')

tenant_id = '12345678-1234-1234-1234-123456789012'
tenant_name = 'test-tenant'
display_name = 'Test Tenant'

# ステップ1: 検証(削除は行わない)
try:
    deleter.delete_tenant(tenant_id, tenant_name, display_name, confirm=False)
except ValueError as e:
    print(f"検証に失敗しました: {e}")
    exit(1)

# ステップ2: 削除を確定(準備ができたらコメントアウトを解除)
# result = deleter.delete_tenant(tenant_id, tenant_name, display_name, confirm=True)
# print(f"削除完了: {result['message']}")

JavaScript

const BASE_URL = 'https://your-mindzie-instance.com';

class TenantDeleter {
  constructor(globalApiKey) {
    this.headers = {
      'Authorization': `Bearer ${globalApiKey}`,
      'Content-Type': 'application/json'
    };
  }

  async getTenant(tenantId) {
    const url = `${BASE_URL}/api/tenant/${tenantId}`;
    const response = await fetch(url, { headers: this.headers });
    if (!response.ok) throw new Error(`テナント取得に失敗しました: ${response.status}`);
    return await response.json();
  }

  async deleteTenant(tenantId, name, displayName, confirm = false) {
    if (!confirm) {
      // 検証モード
      const tenant = await this.getTenant(tenantId);

      const errors = [];
      if (tenant.name !== name) {
        errors.push(`名前の不一致: 期待値 '${tenant.name}', 入力値 '${name}'`);
      }
      if (tenant.displayName !== displayName) {
        errors.push(`表示名の不一致: 期待値 '${tenant.displayName}', 入力値 '${displayName}'`);
      }

      if (errors.length > 0) {
        throw new Error('検証失敗:\n' + errors.join('\n'));
      }

      console.log(`テナント '${displayName}' (${name}) の検証に成功しました`);
      console.log(`  - ID: ${tenantId}`);
      console.log(`  - ユーザー数: ${tenant.userCount || 'N/A'}`);
      console.log('\n削除を実行するには confirm=true を指定してください。');
      return null;
    }

    // 削除を実行
    const url = `${BASE_URL}/api/tenant`;
    const response = await fetch(url, {
      method: 'DELETE',
      headers: this.headers,
      body: JSON.stringify({ tenantId, name, displayName })
    });

    if (response.ok) {
      return await response.json();
    }

    const error = await response.json();
    throw new Error(error.error || `削除失敗: ${response.status}`);
  }
}

// 使用例 - 安全な削除ワークフロー
const deleter = new TenantDeleter('your-global-api-key');

const tenantId = '12345678-1234-1234-1234-123456789012';
const tenantName = 'test-tenant';
const displayName = 'Test Tenant';

// ステップ1: 検証(削除は実行しない)
try {
  await deleter.deleteTenant(tenantId, tenantName, displayName, false);
} catch (e) {
  console.error(`検証に失敗しました: ${e.message}`);
  process.exit(1);
}

// ステップ2: 削除を確定(準備ができたらコメント解除)
// const result = await deleter.deleteTenant(tenantId, tenantName, displayName, true);
// console.log(`削除完了: ${result.message}`);

安全な運用のベストプラクティス

削除前に

  1. すべてのデータをエクスポート: Project APIを使い、すべてのプロジェクトを .mpz ファイルとしてエクスポートする
  2. ユーザー確認: 割り当てられているユーザーを確認し通知する
  3. 記録を残す: 何をなぜ削除するのか監査用に記録する
  4. 再確認: テナントID、名前、表示名が正しいか確認する

削除中に

  1. 検証パターンを使う: まず検証のみでdeleteエンドポイントを呼び出す
  2. レスポンス確認: 正しいテナント情報が返されるかチェックする
  3. 慎重に確定: 手動で検証し終えてから confirm=True を指定する

削除後に

  1. 削除の検証: テナント一覧に削除したテナントが表示されないことを確認する
  2. ユーザーのアクセス確認: 該当ユーザーが削除されたテナントにアクセスできないか確認する
  3. ドキュメント更新: システムドキュメントに削除記録を残す

代替案:削除ではなく無効化

データを保持しつつアクセスを制限したい場合は、テナントを無効化することを検討してください:

# テナント無効化(データは保持)
curl -X PUT "https://your-mindzie-instance.com/api/tenant" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "12345678-1234-1234-1234-123456789012",
    "isDisabled": true
  }'

無効化されたテナント:

  • ユーザーはテナントにログインできません
  • すべてのデータは保持されます
  • 後で isDisabled: false に設定して再有効化可能
  • テナント一覧には isDisabled: true として表示されます

通常は永続的な削除より安全な選択肢です。