Files
Primora/apps/backend/db/migrations/00004_telemetry.sql
T
Tomas DvorakandDevin 8cfddf9184 feat: phase 3 observability — IMS telemetry merge
Port IMS telemetry into Primora conventions:

- core.components/core.events (migration 00004 + sqlc) scoped to projects
- POST /api/v1/ingest — single or <=500-event batches via project prm_
  keys (X-Primora-Key alias), auto-registering components, permissive CORS
- Issues: fingerprint-grouped errors with count/severity/first+last seen
- Reads: events w/ filters + before-cursor, components, stats (1h/24h/7d/30d
  bucketed series + component health + totals + metric names), metric
  series (avg/p50/p95/max)
- GET .../telemetry/stream — project-scoped SSE through an in-process hub
  (buffered subs, drop-on-slow, 25s keepalives); ?api_key=/?token= auth
  for EventSource
- Telemetry page: health/issues/events/metrics/integrate tabs, live badge,
  390px-clean; demo stubs
- @primora/client SDK (createPrimoraClient: capture*/heartbeat/installAuto,
  queued keepalive flushes)
- MCP: primora_list_issues/list_events/telemetry_stats/metric_series
- IMS README deprecation note pointing here

Also fixes workspace pins to 0.3.0 — npm ci in the frontend image was
resolving the unpublished 0.2.0 spec from the registry.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 16:21:03 +02:00

33 lines
1.4 KiB
SQL

-- +goose Up
CREATE TABLE core.components (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
name TEXT NOT NULL,
kind TEXT NOT NULL DEFAULT 'other'
CHECK (kind IN ('frontend','backend','database','android','desktop','web','other')),
meta JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (project_id, name)
);
CREATE TABLE core.events (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
component_id UUID REFERENCES core.components(id) ON DELETE SET NULL,
type TEXT NOT NULL CHECK (type IN ('error','metric','log','heartbeat','event')),
severity TEXT NOT NULL DEFAULT 'info',
message TEXT NOT NULL DEFAULT '',
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
fingerprint TEXT NOT NULL DEFAULT '',
ts TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_events_project_ts ON core.events(project_id, ts DESC);
CREATE INDEX idx_events_project_type_ts ON core.events(project_id, type, ts DESC);
CREATE INDEX idx_events_fingerprint ON core.events(fingerprint) WHERE type = 'error';
CREATE INDEX idx_events_metric_name ON core.events(project_id, (payload->>'name')) WHERE type = 'metric';
-- +goose Down
DROP TABLE IF EXISTS core.events;
DROP TABLE IF EXISTS core.components;