@@ -5,8 +5,11 @@ import { LessThan, Repository } from 'typeorm';
55import { TlsCrt } from '../entities/tls-crt.entity' ;
66import { TlsService } from '../tls.service' ;
77import { CertStatus } from '@krakenkey/shared' ;
8+ import type { SubscriptionPlan } from '@krakenkey/shared' ;
89import { MetricsService } from '../../../metrics/metrics.service' ;
910import { EmailService } from '../../../notifications/email.service' ;
11+ import { BillingService } from '../../../billing/billing.service' ;
12+ import { PLAN_LIMITS } from '../../../billing/constants/plan-limits' ;
1013
1114@Injectable ( )
1215export class CertMonitorService {
@@ -18,14 +21,17 @@ export class CertMonitorService {
1821 private readonly tlsService : TlsService ,
1922 private readonly metricsService : MetricsService ,
2023 private readonly emailService : EmailService ,
24+ private readonly billingService : BillingService ,
2125 ) { }
2226
2327 /**
24- * Runs daily at 6 AM. Finds all issued certificates expiring within 30 days
25- * and queues a renewal job for each via BullMQ.
28+ * Runs daily at 6 AM. Finds all issued certificates expiring within their
29+ * tier-specific renewal window and queues a renewal job for each via BullMQ.
30+ * Free tier: 5-day window, paid tiers: 30-day window.
2631 */
2732 @Cron ( CronExpression . EVERY_DAY_AT_6AM )
2833 async checkExpiringCertificates ( ) : Promise < void > {
34+ // Use max window (30 days) for the DB query, then filter per-user by tier
2935 const threshold = new Date ( ) ;
3036 threshold . setDate ( threshold . getDate ( ) + 30 ) ;
3137
@@ -60,11 +66,31 @@ export class CertMonitorService {
6066 `Certificate expiry check: ${ expiring . length } certificate(s) expiring within 30 days` ,
6167 ) ;
6268
69+ // Cache plan lookups to avoid redundant calls for certs owned by the same user
70+ const planCache = new Map < string , SubscriptionPlan > ( ) ;
71+
6372 for ( const cert of expiring ) {
64- if ( cert . user && cert . expiresAt ) {
65- const daysUntilExpiry = Math . floor (
66- ( cert . expiresAt . getTime ( ) - Date . now ( ) ) / 86_400_000 ,
67- ) ;
73+ if ( ! cert . expiresAt ) continue ;
74+
75+ const daysUntilExpiry = Math . floor (
76+ ( cert . expiresAt . getTime ( ) - Date . now ( ) ) / 86_400_000 ,
77+ ) ;
78+
79+ // Determine tier-specific renewal window (cached per user)
80+ let userPlan = planCache . get ( cert . userId ) ;
81+ if ( ! userPlan ) {
82+ userPlan = ( await this . billingService . resolveUserTier (
83+ cert . userId ,
84+ ) ) as SubscriptionPlan ;
85+ planCache . set ( cert . userId , userPlan ) ;
86+ }
87+ const windowDays =
88+ ( PLAN_LIMITS [ userPlan ] ?? PLAN_LIMITS . free ) . renewalWindowDays ;
89+
90+ // Skip certs outside this user's renewal window
91+ if ( daysUntilExpiry > windowDays ) continue ;
92+
93+ if ( cert . user ) {
6894 const commonName =
6995 ( cert . parsedCsr ?. subject ?. find ( ( a ) => a . shortName === 'CN' )
7096 ?. value as string ) ??
0 commit comments