feat(telemetry): geo distribution endpoint (GET /telemetry/geo) with Cosmos GROUP BY aggregation
This commit is contained in:
parent
70fa6f4738
commit
0bfd4bdf22
@ -113,6 +113,35 @@ export async function queryEvents(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function queryGeoDistribution(
|
||||||
|
productId: string,
|
||||||
|
from?: string,
|
||||||
|
to?: string
|
||||||
|
): Promise<Array<{ countryCode: string; count: number }>> {
|
||||||
|
const conditions: string[] = [
|
||||||
|
'c.productId = @productId',
|
||||||
|
'IS_DEFINED(c.countryCode)',
|
||||||
|
'c.countryCode != null',
|
||||||
|
];
|
||||||
|
const parameters: Array<{ name: string; value: string }> = [
|
||||||
|
{ name: '@productId', value: productId },
|
||||||
|
];
|
||||||
|
if (from) {
|
||||||
|
conditions.push('c.occurredAt >= @from');
|
||||||
|
parameters.push({ name: '@from', value: from });
|
||||||
|
}
|
||||||
|
if (to) {
|
||||||
|
conditions.push('c.occurredAt <= @to');
|
||||||
|
parameters.push({ name: '@to', value: to });
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = `SELECT c.countryCode, COUNT(1) AS count FROM c WHERE ${conditions.join(' AND ')} GROUP BY c.countryCode`;
|
||||||
|
const { resources } = await eventsContainer()
|
||||||
|
.items.query<{ countryCode: string; count: number }>({ query, parameters })
|
||||||
|
.fetchAll();
|
||||||
|
return resources.sort((a, b) => b.count - a.count);
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteEventsByUserId(productId: string, userId: string): Promise<number> {
|
export async function deleteEventsByUserId(productId: string, userId: string): Promise<number> {
|
||||||
// Find all events for this user, then delete them
|
// Find all events for this user, then delete them
|
||||||
const { resources } = await eventsContainer()
|
const { resources } = await eventsContainer()
|
||||||
|
|||||||
@ -880,6 +880,15 @@ export async function telemetryRoutes(app: FastifyInstance) {
|
|||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Admin: geo distribution ─────────────────────────────
|
||||||
|
app.get('/telemetry/geo', async req => {
|
||||||
|
requireAdmin(req);
|
||||||
|
const productId = getRequestProductId(req);
|
||||||
|
const { from, to } = req.query as { from?: string; to?: string };
|
||||||
|
const distribution = await repo.queryGeoDistribution(productId, from, to);
|
||||||
|
return { distribution };
|
||||||
|
});
|
||||||
|
|
||||||
// ── Admin: ingestion metrics (JSON) ─────────────────────
|
// ── Admin: ingestion metrics (JSON) ─────────────────────
|
||||||
app.get('/telemetry/metrics', async req => {
|
app.get('/telemetry/metrics', async req => {
|
||||||
requireAdmin(req);
|
requireAdmin(req);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user