Eliminación de Inquilino

Elimina de forma permanente un inquilino y todos los datos relacionados. Esta operación requiere una triple verificación para garantizar la seguridad.

IMPORTANTE: Todos los endpoints en esta página requieren una Clave API Global. Esta es una operación peligrosa que no puede deshacerse.


Eliminar Inquilino

DELETE /api/tenant

Elimina de forma permanente un inquilino y todos los datos relacionados. Requiere triple verificación para garantizar la seguridad.

ADVERTENCIA

Esta operación es IRREVERSIBLE. Todos los datos del inquilino serán eliminados permanentemente, incluyendo:

  • Todos los proyectos y sus conjuntos de datos
  • Todas las investigaciones, cuadernos y paneles
  • Contenedor y archivos de almacenamiento de blobs
  • Registros y configuraciones de la base de datos
  • Asignaciones de usuarios al inquilino (los usuarios mismos no son eliminados)

Siempre exporte los datos importantes antes de eliminar un inquilino.


Cuerpo de la Solicitud

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

Triple Verificación

Los tres identificadores deben coincidir exactamente para que la eliminación proceda:

Campo Tipo Requerido Descripción
tenantId GUID ID del inquilino a eliminar
name string Nombre del inquilino (debe coincidir exactamente)
displayName string Nombre para mostrar (debe coincidir exactamente)

Esta triple verificación previene eliminaciones accidentales al requerir que conozca y confirme los tres identificadores.


Respuesta (200 OK)

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

Campos de la Respuesta

Campo Tipo Descripción
message string Mensaje de confirmación de éxito
tenantName string Nombre del sistema del inquilino eliminado
tenantDisplayName string Nombre para mostrar del inquilino eliminado
storageContainerDeleted boolean Indica si el almacenamiento de blobs fue eliminado

Respuestas de Error

No Encontrado (404)

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

Verificación Fallida (400)

Cuando el nombre del inquilino no coincide:

{
  "error": "Tenant name 'wrong-name' does not match the tenant with ID '12345678-1234-1234-1234-123456789012'. Expected 'acme-corp'.",
  "hint": "All three identifiers (ID, name, display name) must match exactly for safety"
}

Cuando el nombre para mostrar no coincide:

{
  "error": "Display name 'Wrong Name' does not match the tenant with ID '12345678-1234-1234-1234-123456789012'. Expected 'Acme Corporation'.",
  "hint": "All three identifiers (ID, name, display name) must match exactly for safety"
}

No Autorizado (401)

{
  "error": "This endpoint requires a Global API key.",
  "hint": "Global API keys can be created at /admin/global-api-keys"
}

Ejemplos de Implementación

cURL

# Eliminar inquilino (requiere triple verificación)
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):
        """Inicializa con una clave API GLOBAL (no específica de inquilino)."""
        self.headers = {
            'Authorization': f'Bearer {global_api_key}',
            'Content-Type': 'application/json'
        }

    def get_tenant(self, tenant_id):
        """Obtiene detalles del inquilino para verificación."""
        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):
        """
        Elimina un inquilino con triple verificación.

        Args:
            tenant_id: El GUID del inquilino
            name: Nombre del sistema del inquilino (debe coincidir exactamente)
            display_name: Nombre para mostrar del inquilino (debe coincidir exactamente)
            confirm: Establecer en True para realizar la eliminación
        """
        if not confirm:
            # Modo de verificación - comprobar que los valores coinciden
            tenant = self.get_tenant(tenant_id)

            errors = []
            if tenant['name'] != name:
                errors.append(f"Diferencia en el nombre: se esperaba '{tenant['name']}', se obtuvo '{name}'")
            if tenant['displayName'] != display_name:
                errors.append(f"Diferencia en el nombre para mostrar: se esperaba '{tenant['displayName']}', se obtuvo '{display_name}'")

            if errors:
                raise ValueError("Verificación fallida:\n" + "\n".join(errors))

            print(f"Verificación aprobada para inquilino '{display_name}' ({name})")
            print(f"  - ID: {tenant_id}")
            print(f"  - Usuarios: {tenant.get('userCount', 'N/D')}")
            print(f"  - Casos: {tenant.get('caseCount', 'N/D')}")
            print("\nLlame con confirm=True para proceder con la eliminación.")
            return None

        # Realiza la eliminación
        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'Inquilino no encontrado: {tenant_id}')
        elif response.status_code == 400:
            error = response.json()
            raise Exception(f"Verificación fallida: {error.get('error', 'Error desconocido')}")
        else:
            raise Exception(f'Error al eliminar el inquilino: {response.text}')

# Uso - Flujo seguro de eliminación
deleter = TenantDeleter('your-global-api-key')

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

# Paso 1: Verificar (no se elimina)
try:
    deleter.delete_tenant(tenant_id, tenant_name, display_name, confirm=False)
except ValueError as e:
    print(f"Verificación fallida: {e}")
    exit(1)

# Paso 2: Confirmar eliminación (descomente cuando esté listo)
# result = deleter.delete_tenant(tenant_id, tenant_name, display_name, confirm=True)
# print(f"Eliminado: {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(`Error al obtener el inquilino: ${response.status}`);
    return await response.json();
  }

  async deleteTenant(tenantId, name, displayName, confirm = false) {
    if (!confirm) {
      // Modo verificación
      const tenant = await this.getTenant(tenantId);

      const errors = [];
      if (tenant.name !== name) {
        errors.push(`Diferencia en el nombre: se esperaba '${tenant.name}', se obtuvo '${name}'`);
      }
      if (tenant.displayName !== displayName) {
        errors.push(`Diferencia en el nombre para mostrar: se esperaba '${tenant.displayName}', se obtuvo '${displayName}'`);
      }

      if (errors.length > 0) {
        throw new Error('Verificación fallida:\n' + errors.join('\n'));
      }

      console.log(`Verificación aprobada para inquilino '${displayName}' (${name})`);
      console.log(`  - ID: ${tenantId}`);
      console.log(`  - Usuarios: ${tenant.userCount || 'N/D'}`);
      console.log('\nLlame con confirm=true para proceder con la eliminación.');
      return null;
    }

    // Realiza la eliminación
    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 || `Error al eliminar: ${response.status}`);
  }
}

// Uso - Flujo seguro de eliminación
const deleter = new TenantDeleter('your-global-api-key');

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

// Paso 1: Verificar (no se elimina)
try {
  await deleter.deleteTenant(tenantId, tenantName, displayName, false);
} catch (e) {
  console.error(`Verificación fallida: ${e.message}`);
  process.exit(1);
}

// Paso 2: Confirmar eliminación (descomente cuando esté listo)
// const result = await deleter.deleteTenant(tenantId, tenantName, displayName, true);
// console.log(`Eliminado: ${result.message}`);

Mejores Prácticas de Seguridad

Antes de la Eliminación

  1. Exporte Todos los Datos: Use la API de Proyectos para exportar todos los proyectos como archivos .mpz
  2. Verifique los Usuarios: Revise qué usuarios están asignados y notifíquelos
  3. Documente: Registre qué se está eliminando y por qué, para fines de auditoría
  4. Verifique Dos Veces: Asegúrese de que el ID, nombre y nombre para mostrar del inquilino sean correctos

Durante la Eliminación

  1. Use el Patrón de Verificación: Llame al endpoint de eliminación en modo solo verificación primero
  2. Verifique la Respuesta: Asegúrese de que la respuesta muestre la información correcta del inquilino
  3. Confirme Deliberadamente: Solo pase confirm=True tras una verificación manual

Después de la Eliminación

  1. Verifique la Eliminación: Confirme que el inquilino ya no aparece en la lista de inquilinos
  2. Revise los Usuarios: Verifique que los usuarios afectados ya no puedan acceder al inquilino eliminado
  3. Actualice la Documentación: Registre la eliminación en la documentación de su sistema

Alternativa: Deshabilitar en Lugar de Eliminar

Considere deshabilitar un inquilino en lugar de eliminarlo para preservar los datos mientras se evita el acceso:

# Deshabilitar inquilino (preserva datos)
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
  }'

Inquilinos deshabilitados:

  • Los usuarios no pueden iniciar sesión en el inquilino
  • Se conservan todos los datos
  • Pueden habilitarse de nuevo configurando isDisabled: false
  • Aparecen en la lista de inquilinos con isDisabled: true

Esta suele ser una opción más segura que la eliminación permanente.