Skip to content

Commit f8b09da

Browse files
Merge branch 'main' into feat/compute-price-change-helper
2 parents 7330050 + d60e37d commit f8b09da

107 files changed

Lines changed: 7908 additions & 308 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,9 @@ OWNERSHIP_SNAPSHOT_CLEANUP_DRY_RUN=true
5353
OWNERSHIP_SNAPSHOT_RETENTION_DAYS=30
5454
OWNERSHIP_SNAPSHOT_CLEANUP_ENABLED=false
5555
OWNERSHIP_SNAPSHOT_CLEANUP_INTERVAL_MINUTES=60
56+
57+
# Request body size limits (see docs/body-size-limits.md)
58+
BODY_SIZE_LIMIT_DEFAULT=10mb
59+
# BODY_SIZE_LIMIT_AUTH=100kb
60+
# BODY_SIZE_LIMIT_ADMIN=10mb
61+
# BODY_SIZE_LIMIT_CREATORS=10mb

docs/api-endpoints.md

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
# API Endpoint Reference
2+
3+
Base URL: `http://localhost:3000/api/v1`
4+
5+
## Health Endpoints
6+
7+
### GET /health
8+
9+
Simple health check for load balancers.
10+
11+
- **Auth:** None
12+
- **Response:** `200 OK`
13+
14+
```json
15+
{
16+
"success": true,
17+
"message": "OK",
18+
"timestamp": "2025-01-15T10:30:00.000Z"
19+
}
20+
```
21+
22+
### GET /health/ready
23+
24+
Readiness check with dependency probes.
25+
26+
- **Auth:** None
27+
- **Response:** `200 OK` or `503 Service Unavailable`
28+
29+
```json
30+
{
31+
"ready": true,
32+
"timestamp": "2025-01-15T10:30:00.000Z",
33+
"checks": [
34+
{ "name": "database", "status": "ok", "latencyMs": 12 },
35+
{ "name": "cache", "status": "ok" }
36+
]
37+
}
38+
```
39+
40+
### GET /health/detailed
41+
42+
Full diagnostics including memory and system info.
43+
44+
- **Auth:** None
45+
- **Response:** `200 OK`
46+
47+
```json
48+
{
49+
"success": true,
50+
"message": "Access Layer server is running",
51+
"timestamp": "2025-01-15T10:30:00.000Z",
52+
"version": "1.0.0",
53+
"environment": "development",
54+
"uptime": 12345.67,
55+
"memory": { "used": 45.23, "total": 128.5 },
56+
"system": { "platform": "darwin", "nodeVersion": "v20.10.0" },
57+
"database": { "status": "connected", "responseTime": 12 },
58+
"services": [
59+
{ "name": "API Server", "status": "healthy" },
60+
{ "name": "Database", "status": "healthy" }
61+
]
62+
}
63+
```
64+
65+
---
66+
67+
## Auth Endpoints
68+
69+
### POST /auth/login
70+
71+
Authenticate a user.
72+
73+
- **Auth:** None
74+
- **Body:**
75+
76+
```json
77+
{
78+
"email": "user@example.com",
79+
"password": "securepassword"
80+
}
81+
```
82+
83+
- **Response:** `200 OK`
84+
85+
### POST /auth/register
86+
87+
Register a new user.
88+
89+
- **Auth:** None
90+
- **Body:**
91+
92+
```json
93+
{
94+
"email": "user@example.com",
95+
"password": "securepassword",
96+
"name": "User Name"
97+
}
98+
```
99+
100+
- **Response:** `201 Created`
101+
102+
---
103+
104+
## Config Endpoints
105+
106+
### GET /config
107+
108+
Get protocol bootstrap configuration.
109+
110+
- **Auth:** None
111+
- **Response:** `200 OK`
112+
113+
```json
114+
{
115+
"network": "testnet",
116+
"contractAddress": "..."
117+
}
118+
```
119+
120+
---
121+
122+
## Creators Endpoints
123+
124+
### GET /creators
125+
126+
List all creators with pagination.
127+
128+
- **Auth:** None
129+
- **Query Params:**
130+
- `page` (number, default: 1)
131+
- `limit` (number, default: 10)
132+
- **Response:** `200 OK`
133+
134+
```json
135+
{
136+
"creators": [...],
137+
"pagination": {
138+
"page": 1,
139+
"limit": 10,
140+
"total": 100
141+
}
142+
}
143+
```
144+
145+
### GET /creators/:id/stats
146+
147+
Get public stats for a specific creator.
148+
149+
- **Auth:** None
150+
- **Response:** `200 OK`
151+
152+
```json
153+
{
154+
"creatorId": "...",
155+
"totalSales": 150,
156+
"totalEarnings": 12500.50
157+
}
158+
```
159+
160+
---
161+
162+
## Creator Profile Endpoints
163+
164+
### GET /creators/:creatorId/profile
165+
166+
Get creator profile scaffold payload.
167+
168+
- **Auth:** None
169+
- **Response:** `200 OK`
170+
171+
```json
172+
{
173+
"creatorId": "...",
174+
"displayName": "Creator Name",
175+
"bio": "...",
176+
"avatarUrl": "..."
177+
}
178+
```
179+
180+
### PUT /creators/:creatorId/profile
181+
182+
Upsert creator profile.
183+
184+
- **Auth:** Wallet ownership required
185+
- **Headers:**
186+
- `x-wallet-address: <wallet_address>` (must match creator)
187+
- **Body:**
188+
189+
```json
190+
{
191+
"displayName": "New Name",
192+
"bio": "Updated bio",
193+
"avatarUrl": "https://..."
194+
}
195+
```
196+
197+
- **Response:** `200 OK`
198+
199+
---
200+
201+
## Metrics Endpoints
202+
203+
### GET /metrics/queues
204+
205+
Queue depth metrics for indexer workers.
206+
207+
- **Auth:** None
208+
- **Response:** `200 OK`
209+
210+
```json
211+
{
212+
"queues": {
213+
"indexer": { "depth": 42, "processing": 5 },
214+
"notifications": { "depth": 10, "processing": 2 }
215+
}
216+
}
217+
```
218+
219+
---
220+
221+
## Admin Endpoints
222+
223+
### PATCH /admin/creators/:id/metadata
224+
225+
Update creator metadata.
226+
227+
- **Auth:** Admin required
228+
- **Body:**
229+
230+
```json
231+
{
232+
"metadata": { "key": "value" }
233+
}
234+
```
235+
236+
- **Response:** `200 OK`
237+
238+
### POST /admin/indexer/replay
239+
240+
Replay indexer events.
241+
242+
- **Auth:** Admin required
243+
- **Response:** `200 OK`
244+
245+
---
246+
247+
## Common Headers
248+
249+
| Header | Description |
250+
|--------|-------------|
251+
| `x-wallet-address` | Wallet address for ownership verification |
252+
| `Authorization` | Bearer token for authenticated requests |
253+
| `Content-Type` | `application/json` |
254+
255+
## Error Responses
256+
257+
```json
258+
{
259+
"success": false,
260+
"message": "Error description",
261+
"error": "Detailed error (dev only)"
262+
}
263+
```
264+
265+
| Status | Description |
266+
|--------|-------------|
267+
| 400 | Bad request / validation error |
268+
| 401 | Unauthorized |
269+
| 403 | Forbidden |
270+
| 404 | Not found |
271+
| 429 | Rate limit exceeded |
272+
| 500 | Internal server error |
273+
274+
---
275+
276+
See [Local Setup](./local-setup.md) for development environment configuration.

docs/body-size-limits.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Request Body Size Limits
2+
3+
This document describes how JSON request body size limits are configured per route group, their defaults, and how a client is notified when a request exceeds its limit.
4+
5+
## Overview
6+
7+
Every route group mounted in [modules/index.ts](../src/modules/index.ts) gets its own `express.json()` parser via `routeBodySizeLimit(group)`, instead of a single limit applied globally to every endpoint. This lets a group with a legitimate need for a larger (or smaller) payload be tuned independently, without changing the ceiling for the rest of the API.
8+
9+
- **Middleware:** [body-size-limit.middleware.ts](../src/middlewares/body-size-limit.middleware.ts)
10+
- **Applied in:** [modules/index.ts](../src/modules/index.ts) — one `routeBodySizeLimit(group)` call per router mount
11+
- **Error handling:** [body-parse-error.middleware.ts](../src/middlewares/body-parse-error.middleware.ts), mounted after the router in [app.ts](../src/app.ts)
12+
13+
## Default and Overrides
14+
15+
| Group | Env Var | Default (if unset) |
16+
| :----------- | :------------------------- | :------------------------ |
17+
| _(fallback)_ | `BODY_SIZE_LIMIT_DEFAULT` | `10mb` |
18+
| `auth` | `BODY_SIZE_LIMIT_AUTH` | `BODY_SIZE_LIMIT_DEFAULT` |
19+
| `admin` | `BODY_SIZE_LIMIT_ADMIN` | `BODY_SIZE_LIMIT_DEFAULT` |
20+
| `creators` | `BODY_SIZE_LIMIT_CREATORS` | `BODY_SIZE_LIMIT_DEFAULT` |
21+
22+
All other route groups (`health`, `config`, `metrics`, `ledger`, `activity`, `ownership`, `wallets`, `alerts`) always use `BODY_SIZE_LIMIT_DEFAULT` — they don't currently have a dedicated override, since none of their payloads differ meaningfully from the default ceiling.
23+
24+
Limit values accept any size string understood by the [`bytes`](https://www.npmjs.com/package/bytes) package (used internally by `body-parser`), e.g. `'100kb'`, `'1mb'`, `'10mb'`.
25+
26+
`BODY_SIZE_LIMIT_DEFAULT` itself defaults to `10mb`, matching the single global limit this replaced — existing deployments see no behavior change unless they explicitly set new overrides.
27+
28+
## Adding an Override for a New Group
29+
30+
1. Add the env var to `envSchema` in [config.schema.ts](../src/config.schema.ts):
31+
```typescript
32+
BODY_SIZE_LIMIT_METRICS: optionalNonEmptyString,
33+
```
34+
2. Add the group to `BodySizeLimitGroup` and `GROUP_OVERRIDES` in [body-size-limit.middleware.ts](../src/middlewares/body-size-limit.middleware.ts):
35+
36+
```typescript
37+
export type BodySizeLimitGroup =
38+
| 'auth'
39+
| 'admin'
40+
| 'creators'
41+
| 'metrics'
42+
| 'default';
43+
44+
const GROUP_OVERRIDES: Record<
45+
Exclude<BodySizeLimitGroup, 'default'>,
46+
string | undefined
47+
> = {
48+
auth: envConfig.BODY_SIZE_LIMIT_AUTH,
49+
admin: envConfig.BODY_SIZE_LIMIT_ADMIN,
50+
creators: envConfig.BODY_SIZE_LIMIT_CREATORS,
51+
metrics: envConfig.BODY_SIZE_LIMIT_METRICS,
52+
};
53+
```
54+
55+
3. Pass the group name at the mount point in `modules/index.ts`:
56+
```typescript
57+
router.use('/metrics', routeBodySizeLimit('metrics'), metricsRouter);
58+
```
59+
4. Document the new var's default in the table above and in `.env.example`.
60+
61+
## Fail-Fast Behavior
62+
63+
When a request body exceeds its group's limit, `express.json()` never calls the route handler — it raises a body-parser error (`type: 'entity.too.large'`, `status: 413`) before any controller or database code runs. `bodyParseErrorMiddleware` catches this (for mutation methods — `POST`/`PUT`/`PATCH`/`DELETE`) and returns:
64+
65+
```json
66+
{
67+
"success": false,
68+
"code": "BAD_REQUEST",
69+
"message": "Request payload too large"
70+
}
71+
```
72+
73+
with HTTP status `413`. The same error path also logs a structured `body_parse_failure` entry (method, path, request ID, client IP — never the raw body) for observability. This behavior is identical across every route group regardless of its configured limit — only the threshold that triggers it differs.
74+
75+
## Related Documentation
76+
77+
- [Configuration Guide](./configuration.md) — loading environment configuration.
78+
- [Error Code Registry](./ERROR_CODE_REGISTRY.md) — standard API error shapes.
79+
- [Rate Limiting](./rate-limiting.md) — the sibling per-route-group mechanism for request rate, following the same override pattern.

docs/indexer/EVENT_PROCESSING.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,24 @@ The log includes:
5656

5757
Use `processIndexerChainEvents` to dedupe a batch and log once per unique event.
5858

59+
## 5. Structured batch logging
60+
61+
In addition to the per-event log above, `processIndexerChainEvents` emits
62+
exactly two logs per batch — never per individual ledger:
63+
64+
| Log | Level | Fields |
65+
| :------------------------ | :---- | :---------------------------------------------------------------------------------- |
66+
| `indexer_batch_started` | info | `from_ledger`, `to_ledger`, `batch_size` (raw batch size, before dedup) |
67+
| `indexer_batch_completed` | debug | `from_ledger`, `to_ledger`, `events_processed` (unique, after dedup), `duration_ms` |
68+
69+
`from_ledger`/`to_ledger` are the min/max `ledger` values across the events
70+
in the batch (`undefined` if no event in the batch carries a `ledger`).
71+
`duration_ms` is measured with the same monotonic clock used for per-event
72+
timing, from the start of the batch to the completion of the last event.
73+
74+
These logs give operators batch throughput and size at a glance without
75+
requiring per-ledger database queries.
76+
5977
## 3. Error Handling
6078

6179
If an event fails to process after multiple retries, it is moved to the [Dead-Letter Queue (DLQ)](./DLQ_WORKFLOW.md) for manual investigation.

0 commit comments

Comments
 (0)