Cliente: Centrica Soluciones
Proyecto: Nebula ERP — Componente Nebula Vault (DataVault)
Version: 1.0
Fecha: 16 de Abril, 2026
Autor: Carlos Alberto Torres Camargo — Arquitecto de Software
Clasificacion: Uso Interno — Equipo de Desarrollo Frontend
Estado: EN VALIDACION (rama feature en entorno dev)
Guia para el equipo de desarrollo frontend sobre como consumir la API del backend DataVault (Nebula Vault). Incluye URL base, flujo de autenticacion, endpoints disponibles, ejemplos de consumo y manejo de errores.
| Entorno | URL Base |
|---|---|
| Desarrollo | https://api-datavault-legacy-dev.centricasoluciones.com |
| Staging | Pendiente configuracion |
| Produccion | Pendiente configuracion |
Todos los endpoints de negocio se encuentran bajo el prefijo /api:
https://api-datavault-legacy-dev.centricasoluciones.com/api/{modulo}/{recurso}
| Metodo | Endpoint | Descripcion |
|---|---|---|
GET |
/ |
Informacion del servicio (version, estado) |
GET |
/health |
Health check ({"status":"healthy"}) |
El backend DataVault no tiene login propio. La autenticacion se realiza contra Simappe OAuth2 Server y el JWT resultante se usa para todas las peticiones al backend.
Paso 1: Login
+--------+ POST /simappe-oauth2-server/api/v1/login +-----------+
| | ------------------------------------------------>| |
| Front | {"username": "...", "password": "..."} | Simappe |
| | <------------------------------------------------| OAuth2 |
| | {"accessToken": "eyJ...", "refreshToken":...} | |
+--------+ +-----------+
Paso 2: Seleccionar Empresa
+--------+ POST /simappe-oauth2-server/api/v1/ +-----------+
| | jwt-select-company?companyId=7 | |
| Front | ------------------------------------------------>| Simappe |
| | Header: accesstoken: {token_paso_1} | OAuth2 |
| | <------------------------------------------------| |
| | {"accessToken": "eyJ...(con companyId)..."} | |
+--------+ +-----------+
Paso 3: Consumir API DataVault
+--------+ GET /api/preservation/status +-----------+
| | ------------------------------------------------>| |
| Front | Header: Authorization: Bearer {token_paso_2} | DataVault |
| | <------------------------------------------------| Backend |
| | {"total_files": 0, "status_summary": {...}} | |
+--------+ +-----------+
// environment.ts
export const environment = {
simappeOAuth2Url: 'https://api-dev.centricasoluciones.com/simappe-oauth2-server',
datavaultApiUrl: 'https://api-datavault-legacy-dev.centricasoluciones.com/api',
};
// auth.service.ts (ejemplo simplificado)
@Injectable({ providedIn: 'root' })
export class AuthService {
login(username: string, password: string): Observable<LoginResponse> {
return this.http.post<LoginResponse>(
`${environment.simappeOAuth2Url}/api/v1/login`,
{ username, password }
);
}
selectCompany(companyId: number, accessToken: string): Observable<LoginResponse> {
return this.http.post<LoginResponse>(
`${environment.simappeOAuth2Url}/api/v1/jwt-select-company`,
null,
{
params: { companyId: companyId.toString() },
headers: { accesstoken: accessToken }
}
);
}
}
// datavault.interceptor.ts
@Injectable()
export class DataVaultInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken(); // JWT del paso 2
if (req.url.startsWith(environment.datavaultApiUrl)) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(req);
}
}
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyQGVtYWls...
El JWT debe contener:
sub: email del usuarioUser.companyId: ID de la empresa seleccionadaUser.roles: array de roles del usuarioexp: timestamp de expiracion/api/ingest)| Metodo | Endpoint | Descripcion | Roles |
|---|---|---|---|
GET |
/projects |
Lista proyectos del repositorio | admin_empresa, archivista |
POST |
/projects |
Crea nuevo proyecto | admin_empresa, archivista |
POST |
/projects/{project}/subfolders |
Crea subcarpeta en proyecto | admin_empresa, archivista |
GET |
/projects/{project}/subfolders |
Lista subcarpetas de un proyecto | admin_empresa, archivista |
POST |
/upload |
Sube archivo al area de ingesta | admin_empresa, archivista |
POST |
/process |
Procesa SIP subido | admin_empresa, archivista |
GET |
/status |
Estado de la ultima ingesta | admin_empresa, archivista |
Ejemplo — Listar proyectos:
// Angular service
getProjects(): Observable<string[]> {
return this.http.get<string[]>(`${this.apiUrl}/ingest/projects`);
}
// Respuesta exitosa (200)
["DIGITALIZACION", "TRANSFERENCIA_2022", "CORRESPONDENCIA"]
/api/preservation)| Metodo | Endpoint | Descripcion | Roles |
|---|---|---|---|
GET |
/status |
Resumen de estado de preservacion | admin_empresa, archivista, superadmin |
GET |
/file?file_path=... |
Estado de un archivo especifico | superadmin |
POST |
/diagnose/{file_path} |
Diagnostica problemas de preservacion | superadmin |
POST |
/repair/{file_path} |
Repara problemas detectados | superadmin |
POST |
/verify |
Solicita verificacion de integridad | superadmin |
Ejemplo — Estado de preservacion:
getPreservationStatus(): Observable<PreservationStatus> {
return this.http.get<PreservationStatus>(`${this.apiUrl}/preservation/status`);
}
// Respuesta exitosa (200)
{
"total_files": 42,
"status_summary": {
"optimal": 38,
"attention": 3,
"critical": 1,
"unverified": 0
},
"last_verification_run": "2026-04-15T14:30:00Z",
"last_updated": "2026-04-15T14:35:00Z"
}
/api/oais)| Metodo | Endpoint | Descripcion | Roles |
|---|---|---|---|
GET |
/inspect/{document_id}?document_type=ingest |
Inspecciona AIP fisico | admin_empresa, archivista |
POST |
/apply-legal-hold/{document_id}?legal_hold_id=X |
Aplica bloqueo fisico | admin_empresa, archivista |
POST |
/remove-legal-hold/{document_id}?legal_hold_id=X |
Remueve bloqueo fisico | admin_empresa, archivista |
POST |
/apply-retention/{document_id}?policy_id=X&... |
Aplica retencion fisica | admin_empresa, archivista |
Ejemplo — Inspeccionar AIP:
inspectAIP(documentId: string, type: string = 'ingest'): Observable<AIPInspection> {
return this.http.get<AIPInspection>(
`${this.apiUrl}/oais/inspect/${documentId}`,
{ params: { document_type: type } }
);
}
// Respuesta exitosa (200)
{
"existe": true,
"document_id": "abc-123-def",
"document_type": "ingest",
"ruta": "/home/egs/datavault/Repositorio/DIGITALIZACION/AIP/abc-123-def.zip",
"tipo": "zip",
"tamano": 15728640,
"fecha_modificacion": "2026-04-10T08:15:00",
"proyecto": "DIGITALIZACION",
"permisos": "0644",
"checksum": "sha256:a1b2c3d4e5f6...",
"legal_hold_activo": false,
"lock_files": [],
"retencion_activa": true,
"retention_file": "abc-123-def.retention.json",
"metadata": {
"original_filename": "documento.pdf",
"creation_date": "2026-04-10"
},
"fecha_inspeccion": "2026-04-16T13:45:00"
}
/api/legal-hold)| Metodo | Endpoint | Descripcion | Roles |
|---|---|---|---|
GET |
/ |
Lista casos de Legal Hold | admin_empresa, archivista |
POST |
/ |
Crea caso de Legal Hold | admin_empresa |
GET |
/{legal_hold_id} |
Detalle de un caso | admin_empresa, archivista |
PUT |
/{legal_hold_id} |
Actualiza caso | admin_empresa |
POST |
/{legal_hold_id}/documents |
Agrega documento a caso | admin_empresa |
DELETE |
/{legal_hold_id}/documents/{doc_id} |
Remueve documento de caso | admin_empresa |
/api/document-retention)| Metodo | Endpoint | Descripcion | Roles |
|---|---|---|---|
GET |
/policies |
Lista politicas de retencion | admin_empresa, archivista |
POST |
/policies |
Crea politica de retencion | admin_empresa |
GET |
/policies/{policy_id} |
Detalle de politica | admin_empresa, archivista |
PUT |
/policies/{policy_id} |
Actualiza politica | admin_empresa |
POST |
/apply/{document_id} |
Aplica retencion a documento | admin_empresa, archivista |
POST |
/dispose/{document_id} |
Ejecuta disposicion | admin_empresa |
/api/cloud/daemon)Nota: Estos endpoints requieren rol superadmin.
| Metodo | Endpoint | Descripcion |
|---|---|---|
GET |
/daemon/status/all |
Estado de todos los daemons |
GET |
/daemon/status?daemon_name=X |
Estado de un daemon |
POST |
/daemon/start?daemon_name=X |
Iniciar daemon |
POST |
/daemon/stop?daemon_name=X |
Detener daemon |
POST |
/daemon/restart?daemon_name=X |
Reiniciar daemon |
GET |
/daemon/stats?daemon_name=X |
Metricas CPU/memoria |
GET |
/daemon/logs?daemon_name=X&lines=50 |
Logs recientes |
Valores validos para daemon_name:
oais_watchercloud_sync_daemon_aipcloud_sync_daemon_sippreservation_verifierEjemplo — Panel de administracion de daemons:
// daemon.service.ts
getAllDaemonStatus(): Observable<Record<string, DaemonStatus>> {
return this.http.get<Record<string, DaemonStatus>>(
`${this.apiUrl}/cloud/daemon/status/all`
);
}
restartDaemon(name: string): Observable<DaemonResponse> {
return this.http.post<DaemonResponse>(
`${this.apiUrl}/cloud/daemon/restart`,
null,
{ params: { daemon_name: name } }
);
}
getDaemonLogs(name: string, lines: number = 50): Observable<DaemonLogs> {
return this.http.get<DaemonLogs>(
`${this.apiUrl}/cloud/daemon/logs`,
{ params: { daemon_name: name, lines: lines.toString() } }
);
}
| Codigo | Significado | Accion Frontend |
|---|---|---|
200 |
Exito | Procesar respuesta |
400 |
Error de negocio (datos invalidos, AIP no encontrado) | Mostrar detail al usuario |
401 |
Token invalido o expirado | Redirigir a login |
403 |
Sin permisos (rol insuficiente) | Mostrar mensaje de acceso denegado |
404 |
Recurso no encontrado | Mostrar mensaje apropiado |
422 |
Error de validacion (Pydantic) | Mostrar errores de campo |
500 |
Error interno del servidor | Mostrar error generico, logear detalles |
// Error de negocio (400)
{
"detail": "AIP no encontrado en el servidor para documento test-doc"
}
// Error de validacion (422)
{
"detail": [
{
"type": "missing",
"loc": ["query", "legal_hold_id"],
"msg": "Field required"
}
]
}
// Error interno (500)
{
"detail": "Error inspeccionando AIP: [descripcion del error]"
}
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
this.authService.logout();
this.router.navigate(['/login']);
}
const message = error.error?.detail || 'Error inesperado del servidor';
this.notificationService.showError(message);
return throwError(() => error);
})
);
}
}
// preservation.types.ts
export interface PreservationStatus {
total_files: number;
status_summary: {
optimal: number;
attention: number;
critical: number;
unverified: number;
};
last_verification_run: string | null;
last_updated: string | null;
message?: string;
}
// oais.types.ts
export interface AIPInspection {
existe: boolean;
document_id: string;
document_type: string;
ruta: string | null;
tipo: string | null;
tamano: number | null;
fecha_modificacion: string | null;
proyecto: string | null;
permisos: string | null;
checksum: string | null;
legal_hold_activo: boolean | null;
lock_files: string[] | null;
retencion_activa: boolean | null;
retention_file: string | null;
metadata: Record<string, any> | null;
fecha_inspeccion: string | null;
mensaje: string | null;
error: string | null;
}
// daemon.types.ts
export interface DaemonStatus {
is_running: boolean;
status: string;
daemon_name: string;
container_name: string;
container_id?: string;
image?: string;
created?: string;
started_at?: string;
message?: string;
}
export interface DaemonLogs {
daemon_name: string;
lines: number;
logs: string[];
}
export interface DaemonStats {
daemon_name: string;
cpu_percent: number;
memory_usage: number;
memory_limit: number;
memory_percent: number;
}
El backend acepta peticiones desde:
| Origen | Entorno |
|---|---|
http://localhost:80 |
Desarrollo local |
http://localhost:3000 |
Desarrollo local (Next.js dev) |
http://localhost:3001 |
Desarrollo local (alternativo) |
https://datavaultweb.com |
Produccion |
Si el frontend Angular se ejecuta en un puerto distinto, sera necesario agregar el origen a la configuracion CORS del backend.
Los archivos subidos se sirven desde:
GET /uploads/{filename}
Estos archivos estan disponibles sin autenticacion (servido por FastAPI StaticFiles). Evaluar si se requiere proteccion.
El backend expone documentacion interactiva Swagger en:
| Herramienta | URL |
|---|---|
| Swagger UI | https://api-datavault-legacy-dev.centricasoluciones.com/docs |
| ReDoc | https://api-datavault-legacy-dev.centricasoluciones.com/redoc |
Usar Swagger UI para probar endpoints directamente con el JWT (boton "Authorize" → pegar token).
environment.ts con URL base del backendHttpInterceptor para inyectar header Authorization: BearerDepartamento de Arquitectura — Centrica Soluciones