krz/hutch-stats

clone: git clone https://gitbay.org/krz/hutch-stats.git

main: API.txt · raw

  1# API Reference
  2
  3`srht-contrib` exposes a small HTTP JSON API for health checks, contribution calendar reads, manual polling, and optional tracked repository management.
  4
  5Base URL examples:
  6
  7- Local development: `http://127.0.0.1:8000`
  8- Deployed example: `https://hutch-stats.example.com`
  9
 10Content type:
 11
 12- Request bodies: `application/json`
 13- Response bodies: `application/json`
 14
 15Authentication:
 16
 17- Public endpoints:
 18  - `GET /health`
 19  - `GET /api/contributions/{actor}`
 20  - `GET /api/contributions/{actor}/stats`
 21- Protected endpoints require `X-API-Key`:
 22  - `POST /api/contributions/poll`
 23  - all `/api/repositories*`
 24
 25Example protected header:
 26
 27```http
 28X-API-Key: your-api-key
 29```
 30
 31## Common Conventions
 32
 33Actors:
 34
 35- Actors are SourceHut canonical names such as `~your-user`.
 36- Actor aliases may resolve to the canonical actor through configured alias mappings.
 37
 38Dates:
 39
 40- Query date format is `YYYY-MM-DD`.
 41- `year` and `from`/`to` are mutually exclusive on contribution endpoints.
 42
 43Contribution ranges:
 44
 45- Contribution read endpoints return zero-filled days, so clients do not need to patch missing dates.
 46- Public contribution reads also register the actor for background indexing. A first lookup may therefore return an empty graph while the scheduler catches up.
 47- Clients may add `prioritize_self=true` on contribution read endpoints to explicitly request temporary indexing priority for the signed-in user's own graph.
 48- Incremental indexing and one-year backfill are separate. An actor can be recently indexed before the retained one-year window is fully filled in.
 49- The service only retains and backfills the most recent 365 days of activity.
 50
 51Background polling:
 52
 53- When `ENABLE_SCHEDULER=true`, the service runs one poll immediately at startup and then continues polling on `POLL_INTERVAL_SECONDS`.
 54- The scheduler always seeds `DEFAULT_ACTOR` as a known actor.
 55- Public contribution reads register additional actors for later background polling.
 56- The scheduler only processes due actors, up to `DISCOVERY_BATCH_SIZE` per pass.
 57- Scheduled first indexing skips bounded one-year backfill work, then drains backfill in later scheduled passes so newly requested actors become indexed sooner.
 58- Manual polling remains available through `POST /api/contributions/poll`.
 59
 60Repository names:
 61
 62- Repository create/update accepts either:
 63  - shorthand `repo-name`
 64  - canonical `~owner/repo-name`
 65- Stored repository names are normalized to canonical `~owner/repo-name` form.
 66- Git polling auto-discovers repositories owned by the actor through SourceHut.
 67- Tracked repositories are optional force-includes for git polling; they are not required for normal owned-repository discovery.
 68
 69## Health
 70
 71### `GET /health`
 72
 73Returns a basic service health response.
 74
 75Auth:
 76
 77- Public
 78
 79Response `200 OK`:
 80
 81```json
 82{
 83  "status": "ok"
 84}
 85```
 86
 87## Contributions
 88
 89### `GET /api/contributions/{actor}`
 90
 91Returns a contribution calendar for an actor over a year or explicit date range.
 92
 93Auth:
 94
 95- Public
 96
 97Path parameters:
 98
 99- `actor` string: SourceHut actor, for example `~your-user`
100
101Query parameters:
102
103- `year` integer, optional
104- `from` string `YYYY-MM-DD`, optional
105- `to` string `YYYY-MM-DD`, optional
106- `prioritize_self` boolean, optional
107
108Rules:
109
110- Provide either `year`
111- Or provide both `from` and `to`
112- Do not combine `year` with `from`/`to`
113
114Behavior notes:
115
116- This endpoint resolves aliases to a canonical actor before querying data.
117- This endpoint also registers the actor for background indexing and updates the actor's `last_requested_at` timestamp.
118- When `prioritize_self=true`, registration also applies a temporary scheduler boost so that due polls for that actor run ahead of the normal due queue.
119- The response is always immediate; it does not wait for SourceHut polling to finish.
120- One-year backfill runs in bounded background batches and may take multiple scheduler passes to complete.
121
122Example by year:
123
124```bash
125curl "http://127.0.0.1:8000/api/contributions/~your-user?year=2026"
126```
127
128Example by range:
129
130```bash
131curl "http://127.0.0.1:8000/api/contributions/~your-user?from=2026-03-01&to=2026-04-15"
132```
133
134Response `200 OK`:
135
136```json
137{
138  "actor": "~your-user",
139  "from": "2026-03-01",
140  "to": "2026-04-15",
141  "is_indexed": false,
142  "last_polled_at": null,
143  "indexing_state": "pending",
144  "is_recent_window_backfilled": false,
145  "recent_backfill_state": "in_progress",
146  "recent_backfill_completed_at": null,
147  "days": [
148    { "date": "2026-03-01", "count": 0, "score": 0.0 },
149    { "date": "2026-03-02", "count": 3, "score": 2.5 }
150  ]
151}
152```
153
154Response fields:
155
156- `actor` string: canonical actor after alias resolution
157- `from` string: inclusive start date
158- `to` string: inclusive end date
159- `is_indexed` boolean: whether the service has already completed at least one successful recent/incremental poll for this actor
160- `last_polled_at` string or `null`: most recent successful poll time, if any
161- `indexing_state` string: one of `pending`, `indexed`, or `error`
162- `is_recent_window_backfilled` boolean: whether the service has finished filling the retained one-year history window
163- `recent_backfill_state` string: one of `pending`, `in_progress`, `completed`, or `error`
164- `recent_backfill_completed_at` string or `null`: when one-year backfill completed, if it has
165- `days` array:
166  - `date` string `YYYY-MM-DD`
167  - `count` integer contribution count for the day
168  - `score` float weighted score for the day
169
170Indexing state semantics:
171
172- `pending`: the actor is known but has not completed a successful poll yet
173- `indexed`: at least one successful poll has completed for the actor
174- `error`: the most recent poll attempt for the actor failed
175
176Recent backfill semantics:
177
178- the service only retains and backfills the most recent 365 days of activity
179- `pending`: the actor has not started one-year backfill yet
180- `in_progress`: one-year backfill is actively progressing in bounded background batches
181- `completed`: the retained one-year window is fully backfilled
182- `error`: the most recent backfill attempt failed
183
184Retention notes:
185
186- activity older than 365 days is not retained
187- scheduled polling periodically prunes contribution rows older than the retained window
188
189Possible errors:
190
191- `400 Bad Request` for invalid or conflicting date input
192
193Example `400`:
194
195```json
196{
197  "detail": "Provide `year` or both `from` and `to`."
198}
199```
200
201### `GET /api/contributions/{actor}/stats`
202
203Returns aggregated stats for the same date selection rules as the calendar endpoint.
204
205Auth:
206
207- Public
208
209Path parameters:
210
211- `actor` string
212
213Query parameters:
214
215- `year` integer, optional
216- `from` string `YYYY-MM-DD`, optional
217- `to` string `YYYY-MM-DD`, optional
218- `prioritize_self` boolean, optional
219
220Behavior notes:
221
222- This endpoint has the same actor-registration and alias-resolution behavior as the calendar endpoint.
223- When `prioritize_self=true`, registration also applies the same temporary scheduler boost as the calendar endpoint.
224- This endpoint returns immediately and does not block on SourceHut polling.
225- This endpoint also reflects whether the retained one-year history window has been fully backfilled yet.
226
227Example:
228
229```bash
230curl "http://127.0.0.1:8000/api/contributions/~your-user/stats?from=2026-03-01&to=2026-04-15"
231```
232
233Response `200 OK`:
234
235```json
236{
237  "actor": "~your-user",
238  "from": "2026-03-01",
239  "to": "2026-04-15",
240  "is_indexed": true,
241  "last_polled_at": "2026-04-11T18:05:00Z",
242  "indexing_state": "indexed",
243  "is_recent_window_backfilled": true,
244  "recent_backfill_state": "completed",
245  "recent_backfill_completed_at": "2026-04-11T18:02:00Z",
246  "total_events": 126,
247  "total_score": 116.75,
248  "active_days": 14,
249  "longest_streak": 5,
250  "current_streak": 0
251}
252```
253
254Response fields:
255
256- `actor` string
257- `from` string
258- `to` string
259- `is_indexed` boolean
260- `last_polled_at` string or `null`
261- `indexing_state` string
262- `is_recent_window_backfilled` boolean
263- `recent_backfill_state` string
264- `recent_backfill_completed_at` string or `null`
265- `total_events` integer
266- `total_score` float
267- `active_days` integer
268- `longest_streak` integer
269- `current_streak` integer
270
271Possible errors:
272
273- `400 Bad Request` for invalid or conflicting date input
274
275### `POST /api/contributions/poll`
276
277Triggers a manual SourceHut poll for the given actor and stores any newly discovered events.
278
279Auth:
280
281- Requires `X-API-Key`
282
283Query parameters:
284
285- `actor` string: SourceHut actor to poll
286
287Example:
288
289```bash
290curl -X POST \
291  -H "X-API-Key: your-api-key" \
292  "http://127.0.0.1:8000/api/contributions/poll?actor=~your-user"
293```
294
295Response `200 OK`:
296
297```json
298{
299  "actor": "~your-user",
300  "inserted_events": 57,
301  "services": ["todo", "git"]
302}
303```
304
305Response fields:
306
307- `actor` string: canonical actor after alias resolution
308- `inserted_events` integer: number of newly inserted normalized events
309- `services` array of strings: currently `["todo", "git"]`
310
311Behavior notes:
312
313- Manual polling also updates the actor's indexing metadata.
314- Manual polling also advances one-year backfill by bounded batches for each supported service.
315- Git polling auto-discovers the actor's owned repositories and unions in any configured tracked repositories.
316
317Possible errors:
318
319- `401 Unauthorized` if the API key is missing or invalid
320- `502 Bad Gateway` if polling SourceHut fails
321
322Example `401`:
323
324```json
325{
326  "detail": "Invalid API key."
327}
328```
329
330Example `502`:
331
332```json
333{
334  "detail": "SourceHut polling failed: HTTP error from SourceHut: 502"
335}
336```
337
338## Tracked Repositories
339
340All repository endpoints are protected and require `X-API-Key`.
341
342Tracked repositories are optional force-includes for git polling. Each repository is associated with an actor and stored in canonical `~owner/repo` form.
343
344### `GET /api/repositories`
345
346Lists tracked git repositories.
347
348Auth:
349
350- Requires `X-API-Key`
351
352Query parameters:
353
354- `actor` string, optional: filter to a canonical actor or alias
355
356Example:
357
358```bash
359curl \
360  -H "X-API-Key: your-api-key" \
361  "http://127.0.0.1:8000/api/repositories?actor=~your-user"
362```
363
364Response `200 OK`:
365
366```json
367[
368  {
369    "id": 1,
370    "service": "git",
371    "actor": "~your-user",
372    "repo_name": "~your-user/your-repo"
373  }
374]
375```
376
377### `GET /api/repositories/{repository_id}`
378
379Fetches one tracked repository by numeric ID.
380
381Auth:
382
383- Requires `X-API-Key`
384
385Path parameters:
386
387- `repository_id` integer
388
389Example:
390
391```bash
392curl \
393  -H "X-API-Key: your-api-key" \
394  "http://127.0.0.1:8000/api/repositories/1"
395```
396
397Response `200 OK`:
398
399```json
400{
401  "id": 1,
402  "service": "git",
403  "actor": "~your-user",
404  "repo_name": "~your-user/your-repo"
405}
406```
407
408Possible errors:
409
410- `404 Not Found` if the repository ID does not exist
411
412### `POST /api/repositories`
413
414Creates a tracked repository entry.
415
416Auth:
417
418- Requires `X-API-Key`
419
420Request body:
421
422```json
423{
424  "actor": "~your-user",
425  "repo_name": "your-repo"
426}
427```
428
429Example:
430
431```bash
432curl -X POST \
433  -H "X-API-Key: your-api-key" \
434  -H "Content-Type: application/json" \
435  -d '{"actor":"~your-user","repo_name":"your-repo"}' \
436  "http://127.0.0.1:8000/api/repositories"
437```
438
439Response `201 Created`:
440
441```json
442{
443  "id": 1,
444  "service": "git",
445  "actor": "~your-user",
446  "repo_name": "~your-user/your-repo"
447}
448```
449
450Possible errors:
451
452- `401 Unauthorized` if the API key is missing or invalid
453- `409 Conflict` if the normalized repository already exists for that actor
454- `422 Unprocessable Content` if `actor` or `repo_name` is blank or malformed
455
456### `PATCH /api/repositories/{repository_id}`
457
458Updates an existing tracked repository.
459
460Auth:
461
462- Requires `X-API-Key`
463
464Path parameters:
465
466- `repository_id` integer
467
468Request body:
469
470```json
471{
472  "actor": "~your-user",
473  "repo_name": "~your-user/your-other-repo"
474}
475```
476
477Body rules:
478
479- At least one of `actor` or `repo_name` must be present
480
481Example:
482
483```bash
484curl -X PATCH \
485  -H "X-API-Key: your-api-key" \
486  -H "Content-Type: application/json" \
487  -d '{"repo_name":"~your-user/your-other-repo"}' \
488  "http://127.0.0.1:8000/api/repositories/1"
489```
490
491Response `200 OK`:
492
493```json
494{
495  "id": 1,
496  "service": "git",
497  "actor": "~your-user",
498  "repo_name": "~your-user/your-other-repo"
499}
500```
501
502Possible errors:
503
504- `404 Not Found`
505- `409 Conflict`
506- `422 Unprocessable Content`
507
508### `DELETE /api/repositories/{repository_id}`
509
510Deletes a tracked repository.
511
512Auth:
513
514- Requires `X-API-Key`
515
516Path parameters:
517
518- `repository_id` integer
519
520Example:
521
522```bash
523curl -X DELETE \
524  -H "X-API-Key: your-api-key" \
525  "http://127.0.0.1:8000/api/repositories/1"
526```
527
528Response `204 No Content`
529
530Possible errors:
531
532- `404 Not Found`
533
534## Error Summary
535
536Common status codes:
537
538- `200 OK` successful read or manual poll
539- `201 Created` successful repository creation
540- `204 No Content` successful repository deletion
541- `400 Bad Request` invalid date parameters
542- `401 Unauthorized` missing or invalid API key
543- `404 Not Found` missing repository record
544- `409 Conflict` duplicate repository after normalization
545- `422 Unprocessable Content` invalid repository payload
546- `502 Bad Gateway` upstream SourceHut failure during poll
547
548## OpenAPI
549
550FastAPI also serves an OpenAPI document at:
551
552```text
553/openapi.json
554```
555
556If interactive docs are enabled by your deployment, the standard FastAPI docs may also be available at:
557
558```text
559/docs
560```