|
1 | | -# 🔌 API Documentation |
| 1 | +# API Documentation |
2 | 2 |
|
3 | 3 | The Peer Learning Platform primarily relies on the **Supabase JavaScript Client** for interacting with the database, and a custom **Node.js Express Backend** for secure external API interactions (like the AI assistant). |
4 | 4 |
|
5 | | -## 📡 Supabase Client APIs |
| 5 | +## Supabase Client APIs |
6 | 6 |
|
7 | 7 | Most data operations are performed directly from the React frontend using the `supabase-js` client. RLS (Row-Level Security) policies in the database ensure these requests are secure. |
8 | 8 |
|
@@ -34,7 +34,7 @@ const sendMessage = async (sessionId: string, content: string, userId: string) = |
34 | 34 | }; |
35 | 35 | ``` |
36 | 36 |
|
37 | | -## 🤖 Custom Node.js API (AI Integration) |
| 37 | +## Custom Node.js API (AI Integration) |
38 | 38 |
|
39 | 39 | For operations requiring secure handling of external API keys (e.g., OpenAI/OpenRouter), requests are sent to our custom backend. |
40 | 40 |
|
@@ -67,108 +67,66 @@ Generates an AI summary of a chat session. |
67 | 67 | - Requires a valid Supabase JWT token. |
68 | 68 | - Protected by a custom, in-house rate limiter middleware (`backend/middlewares/rateLimiter.js`) to prevent abuse. |
69 | 69 |
|
70 | | -## 🔔 Cron Endpoints |
| 70 | +## Cron Routes (`/api/cron`) |
71 | 71 |
|
72 | | -All cron endpoints require `Authorization: Bearer <CRON_SECRET>` and are subject to a 5 req/min per-IP rate limit and a 60-second per-route cooldown. Invocations within the cooldown window return `HTTP 429`. |
| 72 | +These endpoints are triggered by a scheduled cron job and protected by the `CRON_SECRET` environment variable. |
73 | 73 |
|
74 | | ---- |
| 74 | +**Auth**: `Authorization: Bearer <CRON_SECRET>` |
75 | 75 |
|
76 | | -### `POST /api/cron/dispatch-notifications` |
| 76 | +All cron requests must supply the `CRON_SECRET` token in the `Authorization` header. Requests without a valid `CRON_SECRET` receive a `401 Unauthorized` response. |
77 | 77 |
|
78 | | -Dequeues up to 100 pending push notifications (`push_sent_at IS NULL`, oldest first) and delivers them to all registered browser push subscriptions for each recipient. |
| 78 | +### `POST /api/cron/dispatch-notifications` |
79 | 79 |
|
80 | | -**Auth:** `Authorization: Bearer <CRON_SECRET>` |
| 80 | +Atomically claims a batch of pending push notifications (up to 100) and dispatches them to subscribed devices. Uses `push_claimed_at` to prevent concurrent invocations from double-delivering the same notification. |
81 | 81 |
|
82 | | -**Response `200`:** |
| 82 | +**Response**: |
83 | 83 | ```json |
84 | | -{ |
85 | | - "sent": 42, |
86 | | - "processed": 45 |
87 | | -} |
| 84 | +{ "sent": 5, "processed": 5 } |
88 | 85 | ``` |
89 | 86 |
|
90 | | -| Field | Description | |
91 | | -|---|---| |
92 | | -| `processed` | Number of notification rows fetched (max 100) | |
93 | | -| `sent` | Number of individual push deliveries that succeeded | |
94 | | - |
95 | | -`processed - sent` reflects push failures (expired subscriptions, VAPID misconfiguration, etc.). Individual failures are absorbed and do not block delivery to other recipients. Each notification row is stamped with `push_sent_at` regardless of delivery outcome. |
96 | | - |
97 | | -**Error responses:** |
98 | | - |
99 | | -| Status | Condition | |
100 | | -|---|---| |
101 | | -| `401` | Missing or malformed `Authorization` header | |
102 | | -| `403` | Secret mismatch | |
103 | | -| `429` | Rate limit or 60-second cooldown exceeded | |
104 | | -| `500` | VAPID keys not configured, or Supabase error | |
105 | | -| `503` | `CRON_SECRET` env var not set on the server | |
106 | | - |
107 | | ---- |
108 | | - |
109 | 87 | ### `POST /api/cron/reminders` |
110 | 88 |
|
111 | | -Inserts `session_reminder` notifications for all sessions with `status = 'upcoming'` whose `start_time` falls within the next 14–16 minutes. Idempotent via `upsert` on `(user_id, entity_id, type)`. |
112 | | - |
113 | | -**Auth:** `Authorization: Bearer <CRON_SECRET>` |
| 89 | +Finds upcoming study sessions starting within the next 15 minutes and inserts `session_reminder` notifications for all participants. |
114 | 90 |
|
115 | | -**Response `200`:** |
| 91 | +**Response**: |
116 | 92 | ```json |
117 | | -{ "inserted": 12 } |
| 93 | +{ "inserted": 3 } |
118 | 94 | ``` |
119 | 95 |
|
120 | | -**Error responses:** same as `/api/cron/dispatch-notifications`. |
121 | | - |
122 | | ---- |
123 | | - |
124 | 96 | ### `POST /api/cron/mentorship-reminders` |
125 | 97 |
|
126 | | -Inserts `mentorship_reminder` notifications for incomplete milestones due within 24 hours or already overdue. Notifies both the mentor and mentee. Idempotent via `upsert` on `(user_id, entity_id, type)`. |
127 | | - |
128 | | -**Auth:** `Authorization: Bearer <CRON_SECRET>` |
| 98 | +Finds incomplete mentorship milestones that are due or overdue within the next 24 hours and inserts `mentorship_reminder` notifications for mentor and mentee. |
129 | 99 |
|
130 | | -**Response `200`:** |
| 100 | +**Response**: |
131 | 101 | ```json |
132 | | -{ "inserted": 4 } |
| 102 | +{ "inserted": 2 } |
133 | 103 | ``` |
134 | 104 |
|
135 | | -**Error responses:** same as `/api/cron/dispatch-notifications`. |
136 | | - |
137 | | ---- |
| 105 | +## Notification Routes (`/api/notifications`) |
138 | 106 |
|
139 | | -## 🔔 Notification Endpoints |
| 107 | +These endpoints support two authentication modes: `WEBHOOK_SECRET` for server-to-server calls, and a standard Supabase JWT for user-initiated calls. |
140 | 108 |
|
141 | | -### `POST /api/notifications/send-push` |
| 109 | +**Auth**: `Authorization: Bearer <WEBHOOK_SECRET>` OR valid Supabase JWT token. |
142 | 110 |
|
143 | | -Delivers a push notification to a single user's registered browser subscriptions. Stale subscriptions (HTTP 410/404 from the push service) are automatically deleted. |
| 111 | +Requests carrying a valid `WEBHOOK_SECRET` bypass user-level auth. Requests without a `WEBHOOK_SECRET` fall back to the standard `requireAuth` middleware which validates the Supabase JWT. |
144 | 112 |
|
145 | | -**Auth:** `Authorization: Bearer <WEBHOOK_SECRET>` |
| 113 | +### `POST /api/notifications/send-push` |
146 | 114 |
|
147 | | -> This endpoint uses a **separate secret** (`WEBHOOK_SECRET`) from the cron endpoints (`CRON_SECRET`). See the Secrets Reference in `docs/smart-notifications.md` for rotation guidance. |
| 115 | +Sends a browser push notification to all subscribed devices for a given `user_id`. |
148 | 116 |
|
149 | | -**Request body:** |
| 117 | +**Request Body**: |
150 | 118 | ```json |
151 | 119 | { |
152 | 120 | "user_id": "uuid", |
153 | | - "title": "string (max 100 chars)", |
154 | | - "body": "string (max 500 chars)", |
155 | | - "action_url": "/optional/path" |
| 121 | + "title": "New message", |
| 122 | + "body": "Alice sent you a message.", |
| 123 | + "action_url": "/messages" |
156 | 124 | } |
157 | 125 | ``` |
158 | 126 |
|
159 | | -**Response `200`:** |
| 127 | +**Response**: |
160 | 128 | ```json |
161 | | -{ |
162 | | - "sent": 1, |
163 | | - "failed": 0 |
164 | | -} |
| 129 | +{ "sent": 1, "failed": 0 } |
165 | 130 | ``` |
166 | 131 |
|
167 | | -**Error responses:** |
168 | | - |
169 | | -| Status | Condition | |
170 | | -|---|---| |
171 | | -| `400` | Missing `user_id`, `title`, or `body`; payload type invalid; content too long | |
172 | | -| `401` | Missing or invalid `Authorization` header | |
173 | | -| `404` | No push subscriptions found for the given `user_id` | |
174 | | -| `500` | VAPID keys not configured, or Supabase error | |
| 132 | +**Security**: Standard users may only send push notifications to themselves (IDOR prevention). Webhook callers authenticated via `WEBHOOK_SECRET` may send to any user. |
0 commit comments