a tool for shared writing and social publishing

fix analytics pipeline

+30 -15
+14 -7
app/api/rpc/[command]/get_publication_analytics.ts
··· 21 21 { supabase }: Pick<Env, "supabase">, 22 22 ) => { 23 23 const identity = await getIdentityData(); 24 - if (!identity?.atp_did || !identity.entitlements?.publication_analytics || !identity.entitlements?.pro_plan_visible) { 24 + if ( 25 + !identity?.atp_did || 26 + !identity.entitlements?.publication_analytics || 27 + !identity.entitlements?.pro_plan_visible 28 + ) { 25 29 return { error: "unauthorized" as const }; 26 30 } 27 31 ··· 32 36 .eq("uri", publication_uri) 33 37 .single(); 34 38 35 - if (!publication || publication.identity_did !== identity.atp_did) { 39 + if (!publication) { 36 40 return { error: "not_found" as const }; 37 41 } 38 42 39 - const domain = publication.publication_domains?.[0]?.domain; 40 - if (!domain) { 43 + const domains = (publication.publication_domains ?? []) 44 + .map((d) => d.domain) 45 + .filter(Boolean) 46 + .join(","); 47 + if (!domains) { 41 48 return { 42 49 result: { traffic: [], topReferrers: [], topPages: [] }, 43 50 }; ··· 45 52 46 53 const [trafficResult, referrersResult, pagesResult] = await Promise.all([ 47 54 tinybird.publicationTraffic.query({ 48 - domain, 55 + domains, 49 56 ...(from ? { date_from: from } : {}), 50 57 ...(to ? { date_to: to } : {}), 51 58 ...(path ? { path } : {}), 52 59 }), 53 60 tinybird.publicationTopReferrers.query({ 54 - domain, 61 + domains, 55 62 ...(from ? { date_from: from } : {}), 56 63 ...(to ? { date_to: to } : {}), 57 64 ...(path ? { path } : {}), 58 65 limit: 10, 59 66 }), 60 67 tinybird.publicationTopPages.query({ 61 - domain, 68 + domains, 62 69 ...(from ? { date_from: from } : {}), 63 70 ...(to ? { date_to: to } : {}), 64 71 limit: 20,
+16 -8
lib/tinybird.ts
··· 19 19 type InferRow, 20 20 type InferParams, 21 21 type InferOutputRow, 22 + TokenDefinition, 22 23 } from "@tinybirdco/sdk"; 24 + 25 + const PROD_READ_TOKEN = { name: "prod_read_token_v1", scopes: ["READ"] }; 23 26 24 27 // ============================================================================ 25 28 // Datasources ··· 87 90 export const publicationTraffic = defineEndpoint("publication_traffic", { 88 91 description: "Daily pageview time series for a publication domain", 89 92 params: { 90 - domain: p.string(), 93 + domains: p.string(), 91 94 date_from: p.string().optional(), 92 95 date_to: p.string().optional(), 93 96 path: p.string().optional(), 94 97 }, 98 + tokens: [PROD_READ_TOKEN], 95 99 nodes: [ 96 100 node({ 97 101 name: "endpoint", ··· 102 106 uniq(deviceId) AS visitors 103 107 FROM analytics_events 104 108 WHERE eventType = 'pageview' 105 - AND domain(origin) = {{String(domain)}} 109 + AND domain(origin) IN splitByChar(',', {{String(domains)}}) 106 110 {% if defined(date_from) %} 107 111 AND fromUnixTimestamp64Milli(timestamp) >= parseDateTimeBestEffort({{String(date_from)}}) 108 112 {% end %} ··· 125 129 }); 126 130 127 131 export type PublicationTrafficParams = InferParams<typeof publicationTraffic>; 128 - export type PublicationTrafficOutput = InferOutputRow<typeof publicationTraffic>; 132 + export type PublicationTrafficOutput = InferOutputRow< 133 + typeof publicationTraffic 134 + >; 129 135 130 136 /** 131 137 * publication_top_referrers – top referring domains for a publication. ··· 133 139 export const publicationTopReferrers = defineEndpoint( 134 140 "publication_top_referrers", 135 141 { 142 + tokens: [PROD_READ_TOKEN], 136 143 description: "Top referrers for a publication domain", 137 144 params: { 138 - domain: p.string(), 145 + domains: p.string(), 139 146 date_from: p.string().optional(), 140 147 date_to: p.string().optional(), 141 148 path: p.string().optional(), ··· 150 157 count() AS pageviews 151 158 FROM analytics_events 152 159 WHERE eventType = 'pageview' 153 - AND domain(origin) = {{String(domain)}} 160 + AND domain(origin) IN splitByChar(',', {{String(domains)}}) 154 161 AND referrer != '' 155 - AND domain(referrer) != {{String(domain)}} 162 + AND domain(referrer) NOT IN splitByChar(',', {{String(domains)}}) 156 163 {% if defined(date_from) %} 157 164 AND fromUnixTimestamp64Milli(timestamp) >= parseDateTimeBestEffort({{String(date_from)}}) 158 165 {% end %} ··· 187 194 */ 188 195 export const publicationTopPages = defineEndpoint("publication_top_pages", { 189 196 description: "Top pages for a publication domain", 197 + tokens: [PROD_READ_TOKEN], 190 198 params: { 191 - domain: p.string(), 199 + domains: p.string(), 192 200 date_from: p.string().optional(), 193 201 date_to: p.string().optional(), 194 202 limit: p.int32().optional(10), ··· 202 210 count() AS pageviews 203 211 FROM analytics_events 204 212 WHERE eventType = 'pageview' 205 - AND domain(origin) = {{String(domain)}} 213 + AND domain(origin) IN splitByChar(',', {{String(domains)}}) 206 214 {% if defined(date_from) %} 207 215 AND fromUnixTimestamp64Milli(timestamp) >= parseDateTimeBestEffort({{String(date_from)}}) 208 216 {% end %}