Skip to content

Commit df3be4c

Browse files
committed
add upnp renewal job
1 parent 80d7152 commit df3be4c

File tree

2 files changed

+36
-20
lines changed

2 files changed

+36
-20
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11

22
export const UPNP_CLIENT_TOKEN = 'UPNP_CLIENT';
3+
export const UPNP_RENEWAL_JOB_TOKEN = 'upnp-renewal';

packages/unraid-api-plugin-connect/src/system/upnp.service.ts

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Inject, Injectable, Logger } from '@nestjs/common';
22
import { ConfigService } from '@nestjs/config';
3+
import { Cron, SchedulerRegistry } from '@nestjs/schedule';
34

45
import { Client, Mapping } from '@runonflux/nat-upnp';
56
import { isDefined } from 'class-validator';
67

78
import { ConfigType } from '../config.entity.js';
89
import { 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()
1213
export 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

Comments
 (0)