11import { Inject , Injectable , Logger } from '@nestjs/common' ;
22import { ConfigService } from '@nestjs/config' ;
3+ import { Cron , SchedulerRegistry } from '@nestjs/schedule' ;
34
45import { Client , Mapping } from '@runonflux/nat-upnp' ;
56import { isDefined } from 'class-validator' ;
67
78import { ConfigType } from '../config.entity.js' ;
89import { ONE_HOUR_SECS } from '../helpers/consts.js' ;
9- import { UPNP_CLIENT_TOKEN } from '../helpers/dependency-injection.js' ;
10+ import { UPNP_CLIENT_TOKEN , UPNP_RENEWAL_JOB_TOKEN } from '../helpers/dependency-injection.js' ;
1011
1112@Injectable ( )
1213export class UpnpService {
@@ -17,34 +18,41 @@ export class UpnpService {
1718
1819 constructor (
1920 private readonly configService : ConfigService < ConfigType > ,
20- @Inject ( UPNP_CLIENT_TOKEN ) private readonly upnpClient : Client
21+ @Inject ( UPNP_CLIENT_TOKEN ) private readonly upnpClient : Client ,
22+ private readonly scheduleRegistry : SchedulerRegistry
2123 ) { }
2224
2325 get enabled ( ) {
2426 return this . #enabled;
2527 }
28+
2629 get wanPort ( ) {
2730 return this . #wanPort;
2831 }
32+
2933 get localPort ( ) {
3034 return this . #localPort;
3135 }
3236
33- private async removeUpnpLease ( ) {
37+ get renewalJob ( ) : ReturnType < typeof this . scheduleRegistry . getCronJob > {
38+ return this . scheduleRegistry . getCronJob ( UPNP_RENEWAL_JOB_TOKEN ) ;
39+ }
40+
41+ private async removeUpnpMapping ( ) {
3442 if ( isDefined ( this . #wanPort) && isDefined ( this . #localPort) ) {
3543 const portMap = {
3644 public : this . #wanPort,
3745 private : this . #localPort,
3846 } ;
3947 try {
4048 const result = await this . upnpClient . removeMapping ( portMap ) ;
41- this . logger . log ( 'UPNP Lease removed %o' , portMap ) ;
42- this . logger . debug ( 'UPNP Lease removal result %O' , result ) ;
49+ this . logger . log ( 'UPNP Mapping removed %o' , portMap ) ;
50+ this . logger . debug ( 'UPNP Mapping removal result %O' , result ) ;
4351 } catch ( error ) {
44- this . logger . warn ( 'UPNP Lease removal failed %O' , error ) ;
52+ this . logger . warn ( 'UPNP Mapping removal failed %O' , error ) ;
4553 }
4654 } else {
47- this . logger . warn ( 'UPNP Lease removal failed. Missing ports: %o' , {
55+ this . logger . warn ( 'UPNP Mapping removal failed. Missing ports: %o' , {
4856 wanPort : this . #wanPort,
4957 localPort : this . #localPort,
5058 } ) ;
@@ -57,7 +65,7 @@ export class UpnpService {
5765 * @param opts
5866 * @returns true if operation succeeds.
5967 */
60- private async renewUpnpLease ( opts ?: {
68+ private async createUpnpMapping ( opts ?: {
6169 publicPort ?: number ;
6270 privatePort ?: number ;
6371 serverName ?: string ;
@@ -76,17 +84,17 @@ export class UpnpService {
7684 } ;
7785 try {
7886 const result = await this . upnpClient . createMapping ( upnpOpts ) ;
79- this . logger . log ( 'UPNP Lease renewed %o' , upnpOpts ) ;
80- this . logger . debug ( 'UPNP Lease renewal result %O' , result ) ;
87+ this . logger . log ( 'UPNP Mapping created %o' , upnpOpts ) ;
88+ this . logger . debug ( 'UPNP Mapping creation result %O' , result ) ;
8189 this . #wanPort = upnpOpts . public ;
8290 this . #localPort = upnpOpts . private ;
8391 this . #enabled = true ;
8492 return true ;
8593 } catch ( error ) {
86- this . logger . warn ( 'UPNP Lease renewal failed %O' , error ) ;
94+ this . logger . warn ( 'UPNP Mapping creation failed %O' , error ) ;
8795 }
8896 } else {
89- this . logger . warn ( 'UPNP Lease renewal failed. Missing ports: %o' , {
97+ this . logger . warn ( 'UPNP Mapping creation failed. Missing ports: %o' , {
9098 publicPort,
9199 privatePort,
92100 } ) ;
@@ -134,19 +142,16 @@ export class UpnpService {
134142 return newWanPort ;
135143 }
136144
137- async enableUpnp ( args ?: { sslPort ?: number ; wanPort ?: number } ) {
145+ async createOrRenewUpnpLease ( args ?: { sslPort ?: number ; wanPort ?: number } ) {
138146 const { sslPort, wanPort } = args ?? { } ;
139147 if ( wanPort !== this . #wanPort || this . #localPort !== sslPort ) {
140- await this . removeUpnpLease ( ) ;
148+ await this . removeUpnpMapping ( ) ;
141149 }
142-
143- // todo: start the renewal job
144-
145150 const wanPortToUse = await this . getWanPortToUse ( args ) ;
146151 this . #wanPort = wanPortToUse ;
147152 const localPortToUse = sslPort ?? this . #localPort;
148153 if ( wanPortToUse && localPortToUse ) {
149- await this . renewUpnpLease ( {
154+ await this . createUpnpMapping ( {
150155 publicPort : wanPortToUse ,
151156 privatePort : localPortToUse ,
152157 } ) ;
@@ -161,10 +166,20 @@ export class UpnpService {
161166 }
162167
163168 async disableUpnp ( ) {
164- // todo: stop the renewal job
165- await this . removeUpnpLease ( ) ;
166169 this . #enabled = false ;
167170 this . #wanPort = undefined ;
168171 this . #localPort = undefined ;
172+ await this . removeUpnpMapping ( ) ;
173+ }
174+
175+ @Cron ( '*/30 * * * *' , { name : UPNP_RENEWAL_JOB_TOKEN } )
176+ async handleUpnpRenewal ( ) {
177+ if ( this . #enabled) {
178+ try {
179+ await this . createOrRenewUpnpLease ( ) ;
180+ } catch ( error ) {
181+ this . logger . error ( '[Job] UPNP Renewal failed %O' , error ) ;
182+ }
183+ }
169184 }
170185}
0 commit comments