@@ -145,206 +145,3 @@ module.exports.getRecentActivity = async (req, res, next) => {
145145 }
146146} ;
147147
148- // ---------------------------------------------------------------------------
149- // ACTIVATION FUNNEL
150- // Returns step-by-step conversion rates for the current developer.
151- // ---------------------------------------------------------------------------
152- module . exports . getActivationFunnel = async ( req , res , next ) => {
153- try {
154- const developerId = req . user . _id ;
155-
156- const FUNNEL_STEPS = [
157- "signup_completed" ,
158- "email_verified" ,
159- "project_created" ,
160- "collection_created" ,
161- "first_api_success" ,
162- ] ;
163- const EVENT_ALIASES = {
164- first_api_success : 'first_api_call' ,
165- } ;
166-
167- // Fetch one event per step (we only need existence, not count)
168- const events = await PlatformEvent . find ( {
169- developerId,
170- event : { $in : [ ...FUNNEL_STEPS , ...Object . keys ( EVENT_ALIASES ) ] } ,
171- } )
172- . sort ( { timestamp : 1 } )
173- . select ( "event timestamp" )
174- . lean ( ) ;
175-
176- const completed = { } ;
177- for ( const e of events ) {
178- const step = EVENT_ALIASES [ e . event ] || e . event ;
179- if ( ! completed [ step ] ) completed [ step ] = e . timestamp ;
180- }
181-
182- const steps = FUNNEL_STEPS . map ( ( step , i ) => ( {
183- step,
184- order : i + 1 ,
185- completed : ! ! completed [ step ] ,
186- completedAt : completed [ step ] || null ,
187- } ) ) ;
188-
189- return new ApiResponse ( { steps } ) . send ( res ) ;
190- } catch ( err ) {
191- next ( err ) ;
192- }
193- } ;
194-
195- // ---------------------------------------------------------------------------
196- // RETENTION (D1 / D7 / D30)
197- // Checks whether the developer was active on Day+1, Day+7, Day+30 after signup.
198- // ---------------------------------------------------------------------------
199- module . exports . getRetention = async ( req , res , next ) => {
200- try {
201- const developerId = req . user . _id ;
202-
203- // Find signup event to anchor the cohort start date
204- const signupEvent = await PlatformEvent . findOne ( {
205- developerId,
206- event : "signup_completed" ,
207- } )
208- . sort ( { timestamp : 1 } )
209- . lean ( ) ;
210-
211- if ( ! signupEvent ) {
212- return new ApiResponse ( {
213- d1 : false ,
214- d7 : false ,
215- d30 : false ,
216- signupDate : null ,
217- } ) . send ( res ) ;
218- }
219-
220- const signupDate = new Date ( signupEvent . timestamp ) ;
221- signupDate . setUTCHours ( 0 , 0 , 0 , 0 ) ;
222-
223- const checkDay = async ( daysAfter ) => {
224- const targetDate = new Date ( signupDate ) ;
225- targetDate . setUTCDate ( targetDate . getUTCDate ( ) + daysAfter ) ;
226- const nextDate = new Date ( targetDate ) ;
227- nextDate . setUTCDate ( nextDate . getUTCDate ( ) + 1 ) ;
228-
229- const activity = await DeveloperActivity . findOne ( {
230- developerId,
231- date : { $gte : targetDate , $lt : nextDate } ,
232- } ) . lean ( ) ;
233- return ! ! activity ;
234- } ;
235-
236- const [ d1 , d7 , d30 ] = await Promise . all ( [
237- checkDay ( 1 ) ,
238- checkDay ( 7 ) ,
239- checkDay ( 30 ) ,
240- ] ) ;
241-
242- return new ApiResponse ( { d1, d7, d30, signupDate } ) . send ( res ) ;
243- } catch ( err ) {
244- next ( err ) ;
245- }
246- } ;
247-
248- // ---------------------------------------------------------------------------
249- // FEATURE ENGAGEMENT (trailing 30 days)
250- // Returns per-feature usage totals across all projects for the developer.
251- // ---------------------------------------------------------------------------
252- module . exports . getEngagement = async ( req , res , next ) => {
253- try {
254- const developerId = req . user . _id ;
255-
256- const thirtyDaysAgo = new Date ( ) ;
257- thirtyDaysAgo . setUTCDate ( thirtyDaysAgo . getUTCDate ( ) - 30 ) ;
258-
259- const agg = await DeveloperActivity . aggregate ( [
260- {
261- $match : {
262- developerId : new mongoose . Types . ObjectId ( developerId ) ,
263- date : { $gte : thirtyDaysAgo } ,
264- } ,
265- } ,
266- {
267- $group : {
268- _id : null ,
269- totalApiCalls : { $sum : "$apiCallCount" } ,
270- totalMailSent : { $sum : "$mailSentCount" } ,
271- totalStorageUploads : { $sum : "$storageUploadsCount" } ,
272- totalWebhooksFired : { $sum : "$webhookTriggeredCount" } ,
273- activeDays : { $sum : 1 } ,
274- allProjectIds : { $push : "$activeProjectIds" } ,
275- } ,
276- } ,
277- ] ) ;
278-
279- const result = agg [ 0 ] || {
280- totalApiCalls : 0 ,
281- totalMailSent : 0 ,
282- totalStorageUploads : 0 ,
283- totalWebhooksFired : 0 ,
284- activeDays : 0 ,
285- } ;
286-
287- // Unique active projects in the 30-day window
288- const flatProjectIds = ( result . allProjectIds || [ ] ) . flat ( ) ;
289- const uniqueActiveProjects = new Set ( flatProjectIds . map ( String ) ) . size ;
290-
291- return new ApiResponse ( {
292- window : "30d" ,
293- totalApiCalls : result . totalApiCalls ,
294- totalMailSent : result . totalMailSent ,
295- totalStorageUploads : result . totalStorageUploads ,
296- totalWebhooksFired : result . totalWebhooksFired ,
297- activeDays : result . activeDays ,
298- uniqueActiveProjects,
299- } ) . send ( res ) ;
300- } catch ( err ) {
301- next ( err ) ;
302- }
303- } ;
304-
305- // ---------------------------------------------------------------------------
306- // NORTH STAR METRIC
307- // "Projects making successful API calls in the last 7 days"
308- // ---------------------------------------------------------------------------
309- module . exports . getNorthStar = async ( req , res , next ) => {
310- try {
311- const developerId = req . user . _id ;
312-
313- const sevenDaysAgo = new Date ( ) ;
314- sevenDaysAgo . setUTCDate ( sevenDaysAgo . getUTCDate ( ) - 7 ) ;
315-
316- // Projects owned by this developer (North Star should be owner-only)
317- const allProjects = await Project . find ( { owner : developerId } )
318- . select ( "_id name" )
319- . lean ( ) ;
320- const projectIds = allProjects . map ( ( p ) => p . _id ) ;
321- const totalProjects = projectIds . length ;
322-
323- if ( totalProjects === 0 ) {
324- return new ApiResponse ( {
325- activeProjects : 0 ,
326- totalProjects : 0 ,
327- percentage : 0 ,
328- } ) . send ( res ) ;
329- }
330-
331- // Projects with at least one 2xx log in the last 7 days
332- const activeProjectIds = await Log . distinct ( "projectId" , {
333- projectId : { $in : projectIds } ,
334- status : { $gte : 200 , $lt : 300 } ,
335- timestamp : { $gte : sevenDaysAgo } ,
336- } ) ;
337-
338- const activeProjects = activeProjectIds . length ;
339- const percentage =
340- totalProjects > 0
341- ? Math . round ( ( activeProjects / totalProjects ) * 100 )
342- : 0 ;
343-
344- return new ApiResponse ( { activeProjects, totalProjects, percentage } ) . send (
345- res ,
346- ) ;
347- } catch ( err ) {
348- next ( err ) ;
349- }
350- } ;
0 commit comments