@@ -17,20 +17,21 @@ import {
1717 getBillingCustomerId ,
1818 trackWebsiteUsage ,
1919} from '../utils/billing' ;
20+ import {
21+ invalidateBasicWebsiteCaches ,
22+ invalidateWebsiteCaches ,
23+ } from '../utils/cache-invalidation' ;
2024
21- // Cache configuration
2225const websiteCache = createDrizzleCache ( { redis, namespace : 'websites' } ) ;
2326const CACHE_DURATION = 60 ; // seconds
2427const TREND_THRESHOLD = 5 ; // percentage
2528
26- // Types
2729interface ChartDataPoint {
2830 websiteId : string ;
2931 date : string ;
3032 value : number ;
3133}
3234
33- // Helper functions
3435const buildFullDomain = ( domain : string , subdomain ?: string ) =>
3536 subdomain ? `${ subdomain } .${ domain } ` : domain ;
3637
@@ -148,7 +149,6 @@ const fetchChartData = async (
148149 return processedData ;
149150} ;
150151
151- // Router definition
152152export const websitesRouter = createTRPCRouter ( {
153153 list : protectedProcedure
154154 . input ( z . object ( { organizationId : z . string ( ) . optional ( ) } ) . default ( { } ) )
@@ -217,7 +217,6 @@ export const websitesRouter = createTRPCRouter({
217217 create : protectedProcedure
218218 . input ( createWebsiteSchema )
219219 . mutation ( async ( { ctx, input } ) => {
220- // Validate organization permissions upfront
221220 if ( input . organizationId ) {
222221 const { success } = await websitesApi . hasPermission ( {
223222 headers : ctx . headers ,
@@ -236,7 +235,6 @@ export const websitesRouter = createTRPCRouter({
236235 input . organizationId
237236 ) ;
238237
239- // Check billing limits before starting transaction
240238 const creationLimitCheck =
241239 await checkAndTrackWebsiteCreation ( billingCustomerId ) ;
242240 if ( ! creationLimitCheck . allowed ) {
@@ -252,9 +250,7 @@ export const websitesRouter = createTRPCRouter({
252250 buildWebsiteFilter ( ctx . user . id , input . organizationId )
253251 ) ;
254252
255- // Execute database operations in transaction
256253 const createdWebsite = await ctx . db . transaction ( async ( tx ) => {
257- // Check for duplicate websites within transaction
258254 const duplicateWebsite = await tx . query . websites . findFirst ( {
259255 where : websiteFilter ,
260256 } ) ;
@@ -285,7 +281,6 @@ export const websitesRouter = createTRPCRouter({
285281 return website ;
286282 } ) ;
287283
288- // Log success after transaction completes
289284 logger . success (
290285 'Website Created' ,
291286 `New website "${ createdWebsite . name } " was created with domain "${ createdWebsite . domain } "` ,
@@ -297,23 +292,20 @@ export const websitesRouter = createTRPCRouter({
297292 }
298293 ) ;
299294
300- // Invalidate cache after successful creation
301- await websiteCache . invalidateByTables ( [ 'websites' ] ) ;
295+ await invalidateBasicWebsiteCaches ( createdWebsite . id , websiteCache ) ;
302296
303297 return createdWebsite ;
304298 } ) ,
305299
306300 update : protectedProcedure
307301 . input ( updateWebsiteSchema )
308302 . mutation ( async ( { ctx, input } ) => {
309- // Authorize access and get billing info before transaction
310303 const websiteToUpdate = await authorizeWebsiteAccess (
311304 ctx ,
312305 input . id ,
313306 'update'
314307 ) ;
315308
316- // Execute update in transaction
317309 const updatedWebsite = await ctx . db . transaction ( async ( tx ) => {
318310 const [ website ] = await tx
319311 . update ( websites )
@@ -331,7 +323,6 @@ export const websitesRouter = createTRPCRouter({
331323 return website ;
332324 } ) ;
333325
334- // Log success after transaction
335326 logger . info (
336327 'Website Updated' ,
337328 `Website "${ websiteToUpdate . name } " was renamed to "${ updatedWebsite . name } "` ,
@@ -343,71 +334,17 @@ export const websitesRouter = createTRPCRouter({
343334 }
344335 ) ;
345336
346- // Invalidate cache after successful update
347- await Promise . all ( [
348- websiteCache . invalidateByTables ( [ 'websites' ] ) ,
349- websiteCache . invalidateByKey ( `getById:${ input . id } ` ) ,
350- ] ) ;
337+ await invalidateBasicWebsiteCaches ( input . id , websiteCache ) ;
351338
352- // If isPublic status changed, invalidate all related caches
353339 if (
354340 input . isPublic !== undefined &&
355341 input . isPublic !== websiteToUpdate . isPublic
356342 ) {
357- // Call the invalidateCaches procedure logic directly
358- await Promise . all ( [
359- // Website caches
360- websiteCache . invalidateByTables ( [ 'websites' ] ) ,
361- websiteCache . invalidateByKey ( `getById:${ input . id } ` ) ,
362-
363- createDrizzleCache ( {
364- redis,
365- namespace : 'website_by_id' ,
366- } ) . invalidateByKey ( `website_by_id:${ input . id } ` ) ,
367- createDrizzleCache ( { redis, namespace : 'auth' } ) . invalidateByKey (
368- `auth:${ ctx . user . id } :${ input . id } `
369- ) ,
370-
371- // Funnel caches
372- createDrizzleCache ( {
373- redis,
374- namespace : 'funnels' ,
375- } ) . invalidateByTables ( [ 'funnelDefinitions' ] ) ,
376- createDrizzleCache ( { redis, namespace : 'funnels' } ) . invalidateByKey (
377- `funnels:list:${ input . id } `
378- ) ,
379- createDrizzleCache ( { redis, namespace : 'funnels' } ) . invalidateByKey (
380- `funnels:listPublic:${ input . id } `
381- ) ,
382-
383- // Goals caches
384- createDrizzleCache ( { redis, namespace : 'goals' } ) . invalidateByTables ( [
385- 'goals' ,
386- ] ) ,
387- createDrizzleCache ( { redis, namespace : 'goals' } ) . invalidateByKey (
388- `goals:list:${ input . id } `
389- ) ,
390-
391- // Autocomplete caches
392- createDrizzleCache ( {
393- redis,
394- namespace : 'autocomplete' ,
395- } ) . invalidateByTables ( [ 'websites' ] ) ,
396-
397- // Mini-charts caches
398- createDrizzleCache ( {
399- redis,
400- namespace : 'mini-charts' ,
401- } ) . invalidateByTables ( [ 'websites' ] ) ,
402- createDrizzleCache ( {
403- redis,
404- namespace : 'mini-charts' ,
405- } ) . invalidateByKey ( `mini-charts:${ ctx . user . id } :${ input . id } ` ) ,
406- createDrizzleCache ( {
407- redis,
408- namespace : 'mini-charts' ,
409- } ) . invalidateByKey ( `mini-charts:public:${ input . id } ` ) ,
410- ] ) ;
343+ await invalidateWebsiteCaches (
344+ input . id ,
345+ ctx . user . id ,
346+ `public status changed to ${ input . isPublic } `
347+ ) ;
411348
412349 logger . info (
413350 'Public status changed - caches invalidated' ,
@@ -427,7 +364,6 @@ export const websitesRouter = createTRPCRouter({
427364 delete : protectedProcedure
428365 . input ( z . object ( { id : z . string ( ) } ) )
429366 . mutation ( async ( { ctx, input } ) => {
430- // Authorize access and get billing info before transaction
431367 const websiteToDelete = await authorizeWebsiteAccess (
432368 ctx ,
433369 input . id ,
@@ -438,16 +374,13 @@ export const websitesRouter = createTRPCRouter({
438374 websiteToDelete . organizationId
439375 ) ;
440376
441- // Execute deletion and billing update in transaction
442377 await ctx . db . transaction ( async ( tx ) => {
443- // Delete website
444378 await tx . delete ( websites ) . where ( eq ( websites . id , input . id ) ) ;
445379
446380 // Track billing usage (decrement)
447381 await trackWebsiteUsage ( billingCustomerId , - 1 ) ;
448382 } ) ;
449383
450- // Log after successful deletion
451384 logger . warning (
452385 'Website Deleted' ,
453386 `Website "${ websiteToDelete . name } " with domain "${ websiteToDelete . domain } " was deleted` ,
@@ -459,22 +392,16 @@ export const websitesRouter = createTRPCRouter({
459392 }
460393 ) ;
461394
462- // Invalidate cache after successful deletion
463- await Promise . all ( [
464- websiteCache . invalidateByTables ( [ 'websites' ] ) ,
465- websiteCache . invalidateByKey ( `getById:${ input . id } ` ) ,
466- ] ) ;
395+ await invalidateBasicWebsiteCaches ( input . id , websiteCache ) ;
467396
468397 return { success : true } ;
469398 } ) ,
470399
471400 transfer : protectedProcedure
472401 . input ( transferWebsiteSchema )
473402 . mutation ( async ( { ctx, input } ) => {
474- // Authorize access before transaction
475403 await authorizeWebsiteAccess ( ctx , input . websiteId , 'update' ) ;
476404
477- // Validate organization permissions upfront
478405 if ( input . organizationId ) {
479406 const { success } = await websitesApi . hasPermission ( {
480407 headers : ctx . headers ,
@@ -488,7 +415,6 @@ export const websitesRouter = createTRPCRouter({
488415 }
489416 }
490417
491- // Execute transfer in transaction
492418 const transferredWebsite = await ctx . db . transaction ( async ( tx ) => {
493419 const [ website ] = await tx
494420 . update ( websites )
@@ -502,7 +428,6 @@ export const websitesRouter = createTRPCRouter({
502428 return website ;
503429 } ) ;
504430
505- // Log success after transaction
506431 logger . info (
507432 'Website Transferred' ,
508433 `Website "${ transferredWebsite . name } " was transferred to organization "${ input . organizationId } "` ,
@@ -513,84 +438,21 @@ export const websitesRouter = createTRPCRouter({
513438 }
514439 ) ;
515440
516- // Invalidate cache after successful transfer
517- await Promise . all ( [
518- websiteCache . invalidateByTables ( [ 'websites' ] ) ,
519- websiteCache . invalidateByKey ( `getById:${ input . websiteId } ` ) ,
520- ] ) ;
441+ await invalidateBasicWebsiteCaches ( input . websiteId , websiteCache ) ;
521442
522443 return transferredWebsite ;
523444 } ) ,
524445
525446 invalidateCaches : protectedProcedure
526447 . input ( z . object ( { websiteId : z . string ( ) } ) )
527448 . mutation ( async ( { ctx, input } ) => {
528- // Authorize access
529449 await authorizeWebsiteAccess ( ctx , input . websiteId , 'update' ) ;
530450
531451 try {
532- // Invalidate all caches related to this website
533- await Promise . all ( [
534- // Website caches
535- websiteCache . invalidateByTables ( [ 'websites' ] ) ,
536- websiteCache . invalidateByKey ( `getById:${ input . websiteId } ` ) ,
537-
538- createDrizzleCache ( {
539- redis,
540- namespace : 'website_by_id' ,
541- } ) . invalidateByKey ( `website_by_id:${ input . websiteId } ` ) ,
542- createDrizzleCache ( { redis, namespace : 'auth' } ) . invalidateByKey (
543- `auth:${ ctx . user . id } :${ input . websiteId } `
544- ) ,
545-
546- // Funnel caches
547- createDrizzleCache ( {
548- redis,
549- namespace : 'funnels' ,
550- } ) . invalidateByTables ( [ 'funnelDefinitions' ] ) ,
551- createDrizzleCache ( { redis, namespace : 'funnels' } ) . invalidateByKey (
552- `funnels:list:${ input . websiteId } `
553- ) ,
554- createDrizzleCache ( { redis, namespace : 'funnels' } ) . invalidateByKey (
555- `funnels:listPublic:${ input . websiteId } `
556- ) ,
557-
558- // Goals caches
559- createDrizzleCache ( { redis, namespace : 'goals' } ) . invalidateByTables ( [
560- 'goals' ,
561- ] ) ,
562- createDrizzleCache ( { redis, namespace : 'goals' } ) . invalidateByKey (
563- `goals:list:${ input . websiteId } `
564- ) ,
565-
566- // Autocomplete caches
567- createDrizzleCache ( {
568- redis,
569- namespace : 'autocomplete' ,
570- } ) . invalidateByTables ( [ 'websites' ] ) ,
571-
572- // Mini-charts caches
573- createDrizzleCache ( {
574- redis,
575- namespace : 'mini-charts' ,
576- } ) . invalidateByTables ( [ 'websites' ] ) ,
577- createDrizzleCache ( {
578- redis,
579- namespace : 'mini-charts' ,
580- } ) . invalidateByKey ( `mini-charts:${ ctx . user . id } :${ input . websiteId } ` ) ,
581- createDrizzleCache ( {
582- redis,
583- namespace : 'mini-charts' ,
584- } ) . invalidateByKey ( `mini-charts:public:${ input . websiteId } ` ) ,
585- ] ) ;
586-
587- logger . info (
588- 'Caches invalidated' ,
589- `All caches invalidated for website ${ input . websiteId } ` ,
590- {
591- websiteId : input . websiteId ,
592- userId : ctx . user . id ,
593- }
452+ await invalidateWebsiteCaches (
453+ input . websiteId ,
454+ ctx . user . id ,
455+ 'manual cache invalidation'
594456 ) ;
595457
596458 return { success : true } ;
0 commit comments