Files

40 lines
2.0 KiB
TypeScript

// umami's provisioner — the adapter that makes umami a provider of the mesh `analytics` interface.
// The reconcile loop and the contributions file are the sdk harness's; this writes only the
// per-service half: how umami creates and removes a tracked site (novox/hq ADR 0039/0040/0048).
//
// The `analytics` interface: a consumer contributes `{ domain }` (the site it wants tracked) and
// receives `{ siteId, snippet, dashboard }`.
//
// **A note on scope (ADR 0048).** ADR 0048 corrects *credential* provisions: the mesh mints a secret
// and the provider creates a login with it. Analytics is not that shape — it mints no secret the
// consumer authenticates with; what the consumer needs back is data umami *generates* (the siteId).
// The credential-provisioner contract returns nothing, so the siteId does not travel back to the
// consumer here. That return path — for a provider that generates data rather than being handed a
// secret — is a separate concern and is not solved by this decision. umami still reconciles its
// sites off the mesh's contributions (it keys on the login the mesh derived), which is what this
// keeps working.
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
import { UmamiClient } from "../client.js";
const umami = UmamiClient.fromEnv();
runProvisioner("analytics", {
async create(p: Provision): Promise<void> {
const domain = String(p.values.domain ?? p.as);
const name = String(p.values.name ?? domain);
const token = await umami.getToken();
// Idempotent: only create the site if it is not already there.
if (!(await umami.findWebsite(token, domain))) {
await umami.createWebsite(token, domain, name);
}
},
async remove(p: { as: string }): Promise<void> {
const token = await umami.getToken();
// Keyed on the mesh-derived login, the one identity the harness carries into removal.
const site = await umami.findWebsite(token, p.as);
if (site) await umami.deleteWebsite(token, site.id);
},
});