Saltar al contenido principal

API de Zertipower

Carpeta: backend/ · Puerto: 3000 · Swagger: http://localhost:3000/api · Tecnología: NestJS 10 + Prisma 5 sobre MariaDB

El núcleo del sistema. La usan el Panel, la Calculadora y el Contador, y es la única pieza que escribe en la base de datos principal.

Qué hace​

  • Identidad y permisos. Emite los tokens de sesión y resuelve los roles.
  • Reparto de la energía. Aplica los coeficientes de reparto de cada comunidad sobre la energía generada y registra las transacciones entre miembros, priorizando el autoconsumo.
  • Datadis. Descarga los consumos horarios reales de cada punto de suministro.
  • Predicciones. Consumo, producción solar y meteorología.
  • Cartera de cada usuario. Deriva la dirección a partir de la identidad de Google, sin depender ya de un servicio externo.
  • Facturación. Integración con Stripe.

Configuración​

Es el módulo con más variables, todas en el bloque environment: del servicio backend en docker-compose.yml. Aquí van agrupadas por tema; el catálogo exhaustivo está en Variables de entorno.

Base de datos​

VariableQué es
DATABASE_URLCadena de conexión que usa Prisma
DB_HOST, DB_USER, DB_PASSWORD, DB_DATABASELos mismos datos, para las consultas SQL directas
DB_PASSWORD sólo admite letras y dígitos

Se interpola tal cual dentro de DATABASE_URL sin codificar, así que un #, /, @, :, ? o % parte la URL y el backend no arranca, con un error que despista mucho: invalid port number in database URL. Genérala con openssl rand -hex 32; no con base64, cuyo alfabeto incluye / y +.

Acceso con Google​

Es el botón «Iniciar sesión con Google» del Panel de Administración. El recorrido completo:

Panel → GET <api>/oauth/google → Google → GET <api>/oauth/google-callback → Panel
VariableQué es¿Obligatoria?
GOOGLE_CLIENT_IDIdentificador del cliente OAuth. Termina en .apps.googleusercontent.com. No es secreto: viaja en la URL a la que Google redirige.No
GOOGLE_CLIENT_SECRETEl secreto de ese mismo cliente.No
GOOGLE_CALLBACK_URLA dónde devuelve Google al usuario.Sí, pero trae valor por defecto

Ninguna de las dos primeras detiene el arranque. Si faltan, el sistema funciona y sólo queda inservible ese botón: se entra con admin / admin123. Van en pareja, eso sí — con una sola no funciona.

Esto no es el acceso del Contador

El Contador no pasa por aquí: delega su login en Zertiauth, que tiene su propio cliente de Google, fuera de este repositorio. Cambiar estas tres variables no afecta al Contador. Ver Contador / Smart Meter.

Cada instalación necesita su propio cliente​

El cliente que venía configurado fue borrado en Google, así que no sirve para nadie: el botón devolvía Error 401: deleted_client — «Access blocked: The OAuth client was deleted». Y como el identificador estaba escrito dentro del docker-compose.yml, que está versionado, no había forma de arreglarlo sin editar un archivo del repositorio.

Ahora las dos llegan del .env y el instalador las pregunta. Si se dejan en blanco, el sistema arranca igual y sólo queda inservible ese botón: se entra con usuario y contraseña.

Cómo crear el cliente​

Hay una guía dedicada que lo explica pantalla por pantalla, sin dar nada por sabido —dónde pulsar, qué elegir y qué pegar—:

👉 Acceso con Google, paso a paso

En resumen, en Google Cloud Console: crear un proyecto, configurar la pantalla de consentimiento, crear un cliente de tipo Aplicación web, y registrar como URI de retorno el valor exacto de GOOGLE_CALLBACK_URL —que es la dirección del backend, no la del Panel—. Después, el ID y el secreto van al .env.

Los permisos que pide el backend son sólo dos, el nombre y el correo (userinfo.profile y userinfo.email). No pide acceso a nada más.

Después basta con reiniciar el servicio; no hace falta reconstruir:

./dev.sh up -d backend

Cuando falla, qué significa​

Lo que dice GoogleQué pasa
deleted_clientEl cliente ya no existe en Google. Es lo que ocurre si se reutiliza el identificador antiguo: crea uno propio.
redirect_uri_mismatchLa URI registrada no es idéntica a GOOGLE_CALLBACK_URL. Compara las dos carácter por carácter.
invalid_clientEl ID y el secreto no son del mismo cliente, o el secreto está mal copiado. Van en pareja: no se pueden mezclar.
Falta client_id (error 400)GOOGLE_CLIENT_ID está vacío en el .env.
«Acceso bloqueado», access_deniedLa aplicación es externa y sigue en pruebas, y esa cuenta no está en la lista de usuarios de prueba.
El primer acceso de cada usuario le asigna su cartera

Al entrar por primera vez con Google, el backend deriva la cartera del usuario a partir de su identificador de Google. Ese identificador lo fija Google y no cambia al cambiar de cliente OAuth, así que crear un cliente nuevo no altera las carteras ya existentes. Lo que sí las cambia es tocar ZERTIAUTH_APP_SECRET o ZERTIAUTH_APP_ID — ver Derivación de carteras.

Mapas y geocodificación​

VariableQué es
GOOGLE_MAPS_API_KEYClave de Google Maps. Opcional.

El backend la usa para convertir una dirección en coordenadas (LocationUtils.getCoordinates). Sin ella el sistema arranca igual y sólo deja de resolver coordenadas: Google responde REQUEST_DENIED y queda anotado en el registro.

La misma clave la usan el Panel y la Calculadora, allí como argumento de construcción. Viene vacía en el repositorio a propósito, porque es público.

Derivación de carteras​

VariableQué es
ZERTIAUTH_APP_IDIdentificador de la aplicación. No es secreto.
ZERTIAUTH_APP_SECRETClave maestra.

La cartera de cada usuario se deriva así:

privateKey = keccak256("<idGoogle>-<ZERTIAUTH_APP_ID>-<ZERTIAUTH_APP_SECRET>")
Cambiarlas invalida todas las carteras

Si cambia cualquiera de las dos, todas las carteras cambian y los usuarios pierden el acceso a sus cuentas y a lo que tengan on-chain. Y como el identificador de Google no es secreto, quien tenga ZERTIAUTH_APP_SECRET puede derivar la clave privada de cualquier usuario.

Hay un detalle que sorprende a quien migra: el identificador de Google es estable por proyecto de Google Cloud. Al crear un proyecto nuevo, el mismo usuario obtiene otro identificador y, por tanto, otra cartera, aunque se conserve el secreto.

Firma de sesiones​

JWT_SECRET lo comparten esta API, la del Contador y la de tiempo real, y el valor debe ser idéntico en las tres, porque unas firman lo que otras verifican.

No es sólo la clave de las sesiones

El backend la reutiliza para cinco cosas distintas:

  1. firmar y verificar los tokens de sesión;
  2. cifrar y descifrar las claves privadas guardadas en la base de datos;
  3. derivar la cartera de cada comunidad (JWT_SECRET + nombre);
  4. derivar carteras por referencia (keccak256(referencia + JWT_SECRET));
  5. cifrar y descifrar las credenciales de Datadis de cada punto de suministro.

Por eso rotarla no «cierra sesiones»: deja ilegibles las claves privadas ya cifradas y las credenciales de Datadis —con lo que el sistema deja de poder descargar consumos— y cambia las direcciones de las carteras derivadas. Sobre un sistema con datos, rotarla es una migración, no un cambio de configuración.

Predicción solar​

VariableQué es
SOLAR_API_URLServicio de predicción de producción
SOLAR_API_AUTH_CODECódigo de autorización. Opcional: sin él el stack arranca, pero esa predicción falla.
SOLAR_API_ALLOW_INSECURE_TLSDesactiva la validación del certificado del servicio
SOLAR_INSTALLATIONS_JSONConfiguración por punto de suministro, p. ej. {"46":{"kwp":32.4}}

Sustituye al proveedor anterior, Wattabit, que dejó de operar: sus credenciales devuelven 401 y las variables RADIATION_API* se mantienen sólo porque el backend las exige para arrancar.

Correo y Stripe​

SMTP_* para el envío de correo —recuperación de contraseña y avisos— y STRIPE_* para la facturación. Con valores de ejemplo el sistema arranca igualmente; sólo falla el envío real y el cobro.

Cómo comprobar que está vivo​

curl http://localhost:3000/api

La documentación Swagger, en http://localhost:3000/api, lista todos los endpoints disponibles.