Files
project.linkedcorp.net/API.md
T
Milton Gonzalez feadd2e715 feat: filtro de severidad (Alta/Media/Baja) en el log de errores
Se asigna sola, sin etiquetar nada a mano: backend y errores de render de React (rompen toda la
pantalla) = alta; errores JS sueltos = media; promesas rechazadas sin manejar = baja (suelen ser
fallos de red puntuales, no criticos). Nuevo filtro en el panel ademas del de origen ya existente.

Migracion 014_error_logs_severity.sql (ALTER TABLE ADD COLUMN IF NOT EXISTS). Probado en dev: las
tres severidades se asignan correctamente segun el origen, un valor invalido del frontend cae en
media, y el filtro combinado con origen funciona.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 14:50:36 -05:00

12 KiB

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

{
  "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

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)