File size: 4,356 Bytes
71b8eb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# API.md — Referencia general PolySignal Backend

Base URL (dev): `http://localhost:7860`  
Base URL (prod, HF Spaces): misma URL que el frontend (mismo origen).

---

## Arranque rápido

```bash
cd backend/
npm install
npm run db:migrate        # solo primera vez
npm run db:seed           # crea usuarios de prueba
npm run dev               # http://localhost:7860
```

Usuarios de prueba: `admin@polysignal.test / Admin123!` y `user@polysignal.test / User123!`

---

## Autenticación

El API usa JWT Bearer. Para obtener un token:

```bash
TOKEN=$(curl -s -X POST http://localhost:7860/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@polysignal.test","password":"Admin123!"}' | jq -r '.data.token')
```

Enviarlo en cada request protegido:

```
Authorization: Bearer <token>
```

---

## Endpoints

### Públicos (sin auth)

| Método | Ruta | Descripción | Doc |
|---|---|---|---|
| `GET` | `/api/v1/health` | Sanity check | [AUTH.md](AUTH.md) |
| `POST` | `/api/v1/auth/login` | Login → JWT | [AUTH.md](AUTH.md) |
| `GET` | `/api/v1/markets` | Lista mercados paginada | [MARKETS.md](MARKETS.md) |
| `GET` | `/api/v1/markets/:id` | Detalle de mercado | [MARKETS.md](MARKETS.md) |
| `GET` | `/api/v1/markets/:id/signal` | Señal AI del mercado | [SIGNALS.md](SIGNALS.md) |

### Protegidos (requieren `Authorization: Bearer <token>`)

| Método | Ruta | Descripción | Doc |
|---|---|---|---|
| `GET` | `/api/v1/auth/me` | Perfil del usuario | [AUTH.md](AUTH.md) |
| `POST` | `/api/v1/positions` | Abrir posición | [POSITIONS.md](POSITIONS.md) |
| `GET` | `/api/v1/positions` | Listar posiciones | [POSITIONS.md](POSITIONS.md) |
| `DELETE` | `/api/v1/positions/:id` | Cerrar posición | [POSITIONS.md](POSITIONS.md) |
| `POST` | `/api/v1/watchlist` | Añadir a watchlist | [WATCHLIST.md](WATCHLIST.md) |
| `GET` | `/api/v1/watchlist` | Listar watchlist | [WATCHLIST.md](WATCHLIST.md) |
| `DELETE` | `/api/v1/watchlist/:marketId` | Eliminar de watchlist | [WATCHLIST.md](WATCHLIST.md) |
| `GET` | `/api/v1/alerts` | Historial de alertas | [ALERTS.md](ALERTS.md) |

---

## Contrato de respuesta

Todas las respuestas siguen el mismo envelope:

```json
// éxito
{ "ok": true, "data": <payload>, "meta": { ...opcional } }

// error
{ "ok": false, "error": { "code": "ERROR_CODE", "message": "...", "details": [...] } }
```

Códigos HTTP: `200` lectura · `201` creación · `204` borrado · `400` validación · `401` no autenticado · `404` no encontrado · `409` conflicto · `429` rate limit · `500` server error.

---

## Rate limiting

- **Global:** 200 req / 15 min / IP
- **`POST /auth/login`:** 5 intentos / 15 min / IP

Headers de respuesta: `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`.

---

## WebSocket (Socket.io)

Conectar al mismo host que el backend:

```js
import { io } from 'socket.io-client';
const socket = io('http://localhost:7860');
```

| Evento | Frecuencia | Descripción | Doc |
|---|---|---|---|
| `market_update` | cada 30s | Precio actualizado de un mercado | [MARKETS.md](MARKETS.md) |
| `ai_signal` | cada 5 min | Nueva señal AI generada | [SIGNALS.md](SIGNALS.md) |
| `price_alert` | cuando se dispara | Alerta de precio threshold | [ALERTS.md](ALERTS.md) |

---

## Importar en Insomnia / Postman

Ver [`insomnia-collection.json`](insomnia-collection.json) — export v4 listo para importar en Insomnia.

Pasos:
1. Abrir Insomnia → Import → File
2. Seleccionar `backend/docs/insomnia-collection.json`
3. Configurar variable de entorno `base_url = http://localhost:7860`
4. Ejecutar `POST /auth/login` y copiar el token a la variable `token`

---

## Variables de entorno completas

Ver [`backend/.env.example`](../.env.example) para la plantilla completa.

| Variable | Obligatoria | Descripción |
|---|---|---|
| `PORT` | Sí | Puerto del servidor (7860 para HF Spaces) |
| `DATABASE_URL` | Sí | Path SQLite: `file:./polysignal.db` |
| `JWT_SECRET` | Sí | Mínimo 32 chars |
| `JWT_EXPIRES_IN` | No | Default `1h` |
| `BCRYPT_ROUNDS` | No | Default `10` |
| `CORS_ORIGIN` | No | Default `http://localhost:5173` |
| `LOG_LEVEL` | No | Default `info` |
| `HF_TOKEN` | No | HuggingFace (señales AI reales) |
| `OPENROUTER_API_KEY` | No | Fallback LLM |
| `FINNHUB_API_KEY` | No | Noticias para señales |
| `TELEGRAM_BOT_TOKEN` | No | Alertas Telegram |