diff --git a/services/platform-service/src/modules/telemetry/routes.ts b/services/platform-service/src/modules/telemetry/routes.ts index de9064a5..4bfe9e1e 100644 --- a/services/platform-service/src/modules/telemetry/routes.ts +++ b/services/platform-service/src/modules/telemetry/routes.ts @@ -697,6 +697,62 @@ export async function telemetryRoutes(app: FastifyInstance) { return { clusters, total: clusters.length }; }); + // ── Admin: policy preview (count matching clients) ────────── + app.post('/telemetry/policies/preview', async req => { + requireAdmin(req); + const productId = getRequestProductId(req); + const targeting = (req.body as Record)?.targeting as + | Record + | undefined; + + // Fetch recent events (last 7 days, up to 500) + const sevenDaysAgo = new Date(Date.now() - 7 * 86400000).toISOString(); + const { events } = await repo.queryEvents(productId, { + from: sevenDaysAgo, + limit: 500, + }); + + // Filter events that match targeting + const matchedIds = new Set(); + for (const evt of events) { + if ( + targeting?.platforms && + Array.isArray(targeting.platforms) && + targeting.platforms.length > 0 + ) { + if (!targeting.platforms.includes(evt.platform)) continue; + } + if ( + targeting?.channels && + Array.isArray(targeting.channels) && + targeting.channels.length > 0 + ) { + if (!targeting.channels.includes(evt.channel)) continue; + } + if ( + targeting?.osFamilies && + Array.isArray(targeting.osFamilies) && + targeting.osFamilies.length > 0 + ) { + if (!targeting.osFamilies.includes(evt.osFamily)) continue; + } + if ( + targeting?.releaseChannels && + Array.isArray(targeting.releaseChannels) && + targeting.releaseChannels.length > 0 + ) { + if (!targeting.releaseChannels.includes(evt.releaseChannel)) continue; + } + matchedIds.add(evt.anonymousInstallId || evt.userId || evt.id); + } + + return { + matchedClients: matchedIds.size, + totalClients: new Set(events.map(e => e.anonymousInstallId || e.userId || e.id)).size, + sampleSize: events.length, + }; + }); + // ── Admin: list policies ────────────────────────────────────── app.get('/telemetry/policies', async req => { requireAdmin(req);