feat(defects): read API — feed, per-section counts and history (T6)

GET /api/defects returns a newest-first feed joined to the project name,
bounded by limit; ?section=ID narrows to one section's history. GET
/api/defects/counts returns { sectionId: n } for the weak-point map. Adds a
(section_id, created_at) index and a db:seed script; ~2k rows query in ~20ms.
This commit is contained in:
Julien Calixte
2026-05-28 00:18:18 +02:00
parent fc958ba59f
commit 999593b426
11 changed files with 384 additions and 14 deletions

View File

@@ -0,0 +1,211 @@
{
"id": "03c0f924-93f1-4194-8e63-2bc4e37f7a8d",
"prevId": "16079f27-2c98-41cc-a954-d09599102874",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.defects": {
"name": "defects",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true
},
"section_id": {
"name": "section_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"project_id": {
"name": "project_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"verbatim": {
"name": "verbatim",
"type": "text",
"primaryKey": false,
"notNull": true
},
"reporter_email": {
"name": "reporter_email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"defects_section_created_idx": {
"name": "defects_section_created_idx",
"columns": [
{
"expression": "section_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "created_at",
"isExpression": false,
"asc": false,
"nulls": "last"
}
],
"isUnique": false,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"defects_project_id_projects_id_fk": {
"name": "defects_project_id_projects_id_fk",
"tableFrom": "defects",
"tableTo": "projects",
"columnsFrom": [
"project_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.projects": {
"name": "projects",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"projects_name_lower_idx": {
"name": "projects_name_lower_idx",
"columns": [
{
"expression": "lower(\"name\")",
"asc": true,
"isExpression": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.push_subscriptions": {
"name": "push_subscriptions",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true
},
"endpoint": {
"name": "endpoint",
"type": "text",
"primaryKey": false,
"notNull": true
},
"p256dh": {
"name": "p256dh",
"type": "text",
"primaryKey": false,
"notNull": true
},
"auth": {
"name": "auth",
"type": "text",
"primaryKey": false,
"notNull": true
},
"reporter_email": {
"name": "reporter_email",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"push_subscriptions_endpoint_unique": {
"name": "push_subscriptions_endpoint_unique",
"nullsNotDistinct": false,
"columns": [
"endpoint"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View File

@@ -15,6 +15,13 @@
"when": 1779916898127,
"tag": "0001_boring_randall",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1779920052613,
"tag": "0002_mixed_chronomancer",
"breakpoints": true
}
]
}