Files
project.linkedcorp.net/API.md
T
Milton Gonzalez d60b4b1d77 feat: version 2.15.0 -- pestaña Compartir en Equipos (acceso de solo lectura)
Admin/superadmin dan acceso de solo lectura (Resumen, Equipos, Software) a otros usuarios;
el backend valida el acceso en cada endpoint y el menu aparece solo a quien lo tenga.
Migracion 026 (device_viewers).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 08:32:53 -05:00

209 lines
13 KiB
Markdown

# API - project.linkedcorp.net
## Base URL
- Desarrollo: `http://localhost:4019/api`
- Producción: `https://project.linkedcorp.net/api`
## Autenticacion
- Tipo: Bearer JWT (`Authorization: Bearer <token>`), emitido por `/api/auth/login`.
- El backend revalida `is_active` del usuario en cada request (no solo la firma del JWT).
## Formato de respuesta
```json
{
"success": true,
"data": {},
"error": null
}
```
## Endpoints principales
### Auth
- `POST /api/auth/login` — `{ email, password }` → `{ token, user }`. Si el usuario no esta
`active` (`pending_verification`/`pending_approval`) o `is_active=0`, `403` con mensaje especifico.
- `POST /api/auth/register` — público. `{ name, email, password }` → si es el primer usuario del
sistema: `{ bootstrap: true, token, user }` (superadmin, sesion directa); si no:
`{ bootstrap: false }` y se envia (o loguea, sin SMTP) un correo de verificación.
- `GET /api/auth/verify-email?token=...` — público. Pasa el usuario a `pending_approval` y notifica
in-app a los `admin`/`superadmin` activos.
- `GET /api/auth/invite?token=...` — público. `{ email }` de la invitación (para prellenar el
formulario), o `400` si es inválida/expiró.
- `POST /api/auth/accept-invite` — público. `{ token, name, password }` → completa la cuenta
creada por `POST /api/projects/:id/members` con `{ email }` (ver arriba): elige nombre y clave,
pasa a `active` directo (sin aprobación — quien invitó ya la vetó) y devuelve `{ token, user }`
con sesión iniciada, igual que el bootstrap del primer usuario.
- `POST /api/auth/forgot-password` — público. `{ email }` → respuesta **siempre genérica**.
- `POST /api/auth/reset-password` — público. `{ token, password }`.
- `PUT /api/auth/change-password` — autenticado. `{ currentPassword, newPassword }`, exige la
clave actual (distinto de `reset-password`, que usa un token de correo sin sesión).
- `POST /api/auth/logout`
- `GET /api/auth/me` — usuario actual
### Usuarios (`/api/users`) — lectura para cualquier autenticado; escritura solo Administrador/Superadministrador
- `GET /api/users` · `POST /api/users` · `PUT /api/users/:id` · `PATCH /api/users/:id/active`
- `POST /api/users/:id/approve` — `{ roleId?, departmentId? }`, solo sobre usuarios `pending_approval`
- `POST /api/users/:id/reject` — borra al usuario `pending_approval` (se audita el email antes)
### Departamentos (`/api/departments`)
- `GET /api/departments` (árbol) · `POST /api/departments` · `PUT /api/departments/:id` · `DELETE /api/departments/:id`
### Proyectos (`/api/projects`)
- `GET /api/projects` (filtros: estado, responsable, prioridad; `trash=true` = papelera, solo
superadmin) · `POST /api/projects`
- `GET /api/projects/:id` · `PUT /api/projects/:id`
- `DELETE /api/projects/:id` — eliminación lógica (`deleted_at`/`deleted_by`), mismo permiso que
editar el proyecto. `POST /api/projects/:id/restore` y `DELETE /api/projects/:id/permanent`
(borrado físico, irreversible, arrastra actividades/comentarios/adjuntos) — ambos solo
superadmin; `permanent` exige que el proyecto ya esté en la papelera (400 si no).
- `POST /api/projects/:id/members` — `{ userId }` (usuario existente) o `{ email }` (si el correo
no pertenece a nadie todavía, crea una cuenta `invited` y le manda un correo de invitación para
que complete su registro; la fila de `project_members` ya queda creada de una vez). `roleInProject`
opcional: `'admin'` = administrador del proyecto. Responde `{ members, invited }`.
- `PUT /api/projects/:id/members/:userId/role` (`{ admin: true|false }`, otorga o quita el rol de
administrador **de ese proyecto** — un proyecto puede tener uno o más administradores, sin
importar el rol global del usuario; nunca toca al `owner`) ·
`DELETE /api/projects/:id/members/:userId`
### Actividades (`/api/activities`)
- `GET /api/projects/:projectId/activities` · `POST /api/projects/:projectId/activities`
- `GET /api/activities/:id` · `PUT /api/activities/:id`
- `DELETE /api/activities/:id` — bloqueado con `400` si la actividad tiene tareas (hay que
eliminarlas primero); limpia comentarios/adjuntos de la actividad de paso (no hay FK física
cruzada). Arrastra `activity_dependencies` por `ON DELETE CASCADE`.
- `POST /api/activities/:id/dependencies` · `DELETE /api/activities/:id/dependencies/:dependsOnId`
- `PUT /api/projects/:projectId/activities/reorder` — `{ ids: [...] }` (orden manual, arrastrar
con el mouse); posiciones 1..N, nunca 0 (0 = "nunca reordenado a mano", ver `sort_order`).
- `PUT /api/activities/:id/move` — `{ projectId }`. Mueve la actividad a otro proyecto; exige
permiso de escritura en ambos proyectos. Tareas/comentarios/adjuntos se mueven solos (referencian
`activity_id`, no `project_id`). Las dependencias existentes (en cualquiera de los dos sentidos)
se eliminan — solo tienen sentido dentro del mismo proyecto. `sort_order` vuelve a 0 en el destino.
- `POST /api/activities/:id/convert-to-project` — `{ projectName, taskActivityName }`. Crea un
proyecto nuevo (`projectName`) y, dentro, una actividad nueva (`taskActivityName`) que recibe
las tareas/comentarios/adjuntos de la actividad original; la original se borra al final (ya sin
tareas). Exige el mismo permiso que crear un proyecto (no solo escribir la actividad). Responde
`{ project, activity }` (201).
### Tareas (`/api/activities/:activityId/tasks`)
- `GET` · `POST` · `PUT /api/tasks/:id` · `PATCH /api/tasks/:id/done` · `DELETE /api/tasks/:id`
- `PUT /api/activities/:activityId/tasks/reorder` — `{ ids: [...] }` (mismo mecanismo de orden
manual que las actividades).
### Tareas recurrentes (`/api/recurring-tasks`) — menú propio, independiente de Proyectos
- `GET` · `POST` · `PUT /api/recurring-tasks/:id` · `DELETE /api/recurring-tasks/:id`
- `POST /api/recurring-tasks/:id/complete` — cierra el ciclo actual y calcula el próximo
vencimiento sumando la periodicidad (`interval_value` + `interval_unit`).
- `POST /api/recurring-tasks/from-task/:taskId` — crea una tarea recurrente a partir de una tarea
puntual de una actividad (la original no se borra); mismo body que `POST /`.
- Body: `{ name, description?, projectId?, responsibleUserId?, intervalUnit: 'days'|'months'|'years',
intervalValue, nextDueDate, endDate?, reminderDaysBefore, reminderHour: 0-23 }`. `endDate` vacío
= se repite sin fin; al completar un ciclo que la supera, `is_active` pasa a `false` solo.
- Recordatorio por correo: un job en el propio proceso del backend revisa cada 15 minutos
(`src/jobs/recurringTaskReminders.js`) y manda el correo cuando `next_due_date -
reminderDaysBefore` cae hoy, a la hora `reminderHour`. Un solo envío por ciclo
(`last_reminder_sent_at`, se limpia al completar el ciclo).
### Comentarios (`/api/comments`)
- `GET /api/comments?entityType=project&entityId=1` · `POST /api/comments`
### Adjuntos (`/api/attachments`)
- `GET /api/attachments?entityType=activity&entityId=1` · `POST /api/attachments` (multipart)
- `GET /api/attachments/:id/download` · `DELETE /api/attachments/:id`
### Notas (`/api/notes`)
- `GET /api/notes` — solo las del usuario autenticado, ordenadas por `updated_at` descendente.
- `POST /api/notes` — `{ content }`. `PUT /api/notes/:id` — `{ content }`. `DELETE /api/notes/:id`.
- Siempre privadas: una nota de otro usuario responde `404` (no `403`, para no revelar que
existe). Sin restricción de rol — cualquier autenticado tiene las suyas.
### Equipos (`/api/devices`) — superadmin / admin / it_manager (tokens: solo superadmin / admin)
- `GET /api/devices?q=&kind=&status=&stale=1&diskFull=1` · `GET /api/devices/:id` (con hardware,
red, software e historial) · `PUT /api/devices/:id` — `{ status, assignedUserId, departmentId, notes }`.
- `GET /api/devices/dashboard` — KPIs y top (SO, tipo, software). `GET /api/devices/software` ·
`GET /api/devices/software/devices?name=` — software transversal.
- `GET /api/devices/access` — `{canView, canEdit, canManage}` del usuario (lo usa el menú). Lectura (`GET` de
dashboard/listado/detalle/software) también para usuarios con acceso compartido.
- `GET/POST /api/devices/viewers` · `DELETE /api/devices/viewers/:userId` — compartir Equipos (admin/superadmin).
- `GET /api/devices/:id/raw` — último inventario crudo del agente.
- `GET /api/devices/tokens/:id/secret` — token en claro (descifrado) para armar el instalador; audita `reveal`.
- `GET/POST /api/devices/tokens` · `PATCH/DELETE /api/devices/tokens/:id` — tokens de ingesta
(el token en claro solo se devuelve en el `POST`).
### Ingesta del agente GLPI (sin JWT; token de ingesta)
- `POST /api/inventory` (alias `POST /front/inventory.php`) — cuerpo JSON/XML, opcionalmente
comprimido (gzip/zlib). Auth: password Basic / `Bearer` / `X-Ingest-Token` / `?token=`.
`401` sin token válido, `400` formato inválido. Responde `{"status":"ok"}` (o `<REPLY/>` en XML).
### Notificaciones (`/api/notifications`)
- `GET /api/notifications` · `PATCH /api/notifications/:id/read` · `PATCH /api/notifications/read-all`
### Búsqueda (`/api/search`)
- `GET /api/search?q=texto` — global: proyectos, actividades y tareas (de cualquier proyecto) que
coincidan. `{ projects: [...], activities: [...], tasks: [...] }`, cada item de actividades/tareas
incluye los datos de su proyecto (y actividad, para tareas) para armar la migaja de pan en el
cliente. `q` con menos de 2 caracteres devuelve listas vacías. Máx 20 resultados por tipo.
- `GET /api/search?q=texto&projectId=1` — acotado a un proyecto: solo `{ activities, tasks }` de
ESE proyecto (sin el dato del proyecto, ya es el contexto).
### Actualizaciones (`/api/updater`) — solo Superadministrador
- `GET /api/updater/status` — compara la `VERSION` local contra `CHANGELOG.md` de `main` en
Gitea (leído de forma anónima por HTTPS, sin token — requiere repo público y vista anónima
habilitada en el servidor de Gitea). `{ currentVersion, upToDate, latestVersion,
overallCriticality?, hasMigrations, pending: [{ version, date, criticality, summary }] }`.
`criticality` se deriva del dígito de versión que cambió (X.Y.Z: 1ro=alta, 2do=media,
3ro=baja), nunca se etiqueta a mano. `hasMigrations` compara los archivos de
`backend/database/migrations/` locales contra los de `main`. Es solo informativo — no aplica
ningún cambio, la actualización real sigue siendo manual.
### Log de errores (`/api/error-logs`)
- `POST /api/error-logs` — público (sin auth, rate-limited a 30/15min), lo usa el frontend para
reportar errores propios (incluso sin sesión). `{ message, stack?, url?, severity? }`
(`severity`: `alta`/`media`/`baja`; si falta o es inválida, cae en `media`). Si llega un token
válido en `Authorization`, se guarda el usuario asociado, sin exigirlo.
- `GET /api/error-logs?source=backend|frontend&severity=alta|media|baja&limit=` — solo
Superadministrador. Lista los errores más recientes (máx 1000 guardados, se recorta solo).
Backend guarda `stack`/`url`/`method`/usuario automáticamente en cada excepción 500 no manejada
(ver `errorHandler.js`) con `severity=alta` siempre — nunca se etiqueta a mano.
- `DELETE /api/error-logs` — solo Superadministrador. Borra todo el historial.
### Dashboard (`/api/dashboard`)
- `GET /api/dashboard/summary` — KPIs (activos, atrasados, pendientes/vencidas/hoy, avance promedio)
- `GET /api/dashboard/calendar?month=2026-09` — actividades por fecha límite
- `GET /api/projects/:id/gantt` — actividades con fechas/avance para el Gantt
### Catálogos (`/api/catalogs`) — lectura general, escritura solo Superadministrador
- `GET/POST/PUT/DELETE /api/catalogs/project-statuses`
- `GET/POST/PUT/DELETE /api/catalogs/activity-statuses`
- `GET/POST/PUT/DELETE /api/catalogs/priorities`
- `GET/POST/PUT/DELETE /api/catalogs/project-types`
### Auditoría (`/api/audit`) — solo Superadministrador / Administrador / Jefe de TI
- `GET /api/audit?entityType=project&entityId=1`
### Configuración SMTP (`/api/settings`) — solo Superadministrador
- `GET /api/settings/smtp` — `{ host, port, user, from, configured }` (nunca la clave)
- `PUT /api/settings/smtp` — `{ host, port, user, from, pass? }` (`pass` vacío = no cambiarla)
- `POST /api/settings/smtp/test` — `{ host, port, user, from, pass?, to }` (`pass` vacío = usa la
guardada). Envía un correo real con los valores del formulario (aunque no se hayan guardado) y
responde `{ sent: true }` o `400` con el error real del servidor SMTP. Rate-limited (10/15min).
### Marca de la empresa (`/api/company`)
- `GET /api/company` — público (sin auth, lo usan login/registro/recuperación). `{ name, hasLogo,
logoVersion }`; `name` por defecto "LinkedCorp" si nunca se configuró.
- `GET /api/company/logo` — público. Devuelve el archivo de imagen del logo, o `404` si no hay
ninguno configurado (el cliente cae al logo estático embebido).
- `PUT /api/company` — solo Superadministrador, multipart: `name?` (texto) y `logo?` (archivo,
PNG/JPG/WEBP/SVG, máx 3MB). Al subir un logo nuevo se borra el archivo anterior (nunca quedan
huérfanos). Guardado en `system_settings` (mismo patrón que SMTP), sin tabla nueva.
## Errores esperados
- `400` validación
- `401` no autenticado / token inválido o expirado
- `403` sin permisos para la acción (según rol)
- `404` no encontrado (incluye cuando el recurso existe pero no pertenece al usuario/alcance)
- `500` error interno (sanitizado, sin detalle de stack en producción)