mirror of
https://github.com/sakaljurgis/pvpn-transmission.git
synced 2026-07-08 20:47:41 +00:00
add worker and notification service
This commit is contained in:
@@ -8,3 +8,5 @@ ALLOWED_INTERFACES=tun0
|
|||||||
DISALLOWED_COUNTRIES=your_country_code_e_g_LV
|
DISALLOWED_COUNTRIES=your_country_code_e_g_LV
|
||||||
DISALLOWED_INTERNAL_IPS=your_internal_ip_e_g_192.168
|
DISALLOWED_INTERNAL_IPS=your_internal_ip_e_g_192.168
|
||||||
DISALLOWED_EXT_IPS=your_external_ip
|
DISALLOWED_EXT_IPS=your_external_ip
|
||||||
|
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your_webhook_url
|
||||||
|
UPDATE_INTERVAL_SECONDS=300
|
||||||
|
|||||||
+16
-84
@@ -1,90 +1,22 @@
|
|||||||
import { Transmission } from './transmission';
|
import { setPortFlow } from './set-port-flow';
|
||||||
import { DockerContainer } from './docker';
|
|
||||||
import { settings } from './settings';
|
import { settings } from './settings';
|
||||||
|
|
||||||
main().then(() => console.log('All done')).catch(console.error);
|
const isWorker = process.argv[2] === 'worker';
|
||||||
|
|
||||||
async function main() {
|
if (isWorker) {
|
||||||
console.log('Starting');
|
//initial timeout is in case of a crash to not flood everything
|
||||||
const transmissionClient = new Transmission(settings.transmission);
|
console.log(`Starting worker, first check in ${settings.updateIntervalMs/1000}s`);
|
||||||
const transmissionContainer = new DockerContainer(
|
setTimeout(worker, settings.updateIntervalMs);
|
||||||
settings.dockerTransmission.service,
|
} else {
|
||||||
settings.dockerTransmission.cwd,
|
setPortFlow()
|
||||||
settings.dockerTransmission.serviceName
|
.then(() => console.log('All done'))
|
||||||
);
|
.catch(console.error);
|
||||||
|
|
||||||
const containerState = await transmissionContainer.getState();
|
|
||||||
if (containerState?.state !== 'running') {
|
|
||||||
console.log('Transmission service is not running');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Gathering info`);
|
//todo - store some state in a file (in case of crash/restart)?
|
||||||
const port = await transmissionClient.getPort();
|
function worker() {
|
||||||
console.log(`Port: ${port}`);
|
setPortFlow()
|
||||||
const portOpen = await transmissionClient.testPortIfOpen();
|
.then(() => console.log('All done'))
|
||||||
console.log(`Port is open: ${portOpen}`);
|
.catch(console.error)
|
||||||
const defaultInterface = await transmissionContainer.getDefaultInterface();
|
.finally(() => setTimeout(worker, settings.updateIntervalMs));
|
||||||
console.log(`Default interface: ${defaultInterface?.interface} Internal ip: ${defaultInterface?.ip}`);
|
|
||||||
const ipInfo = await transmissionContainer.getIpInfo();
|
|
||||||
console.log(`External ip: ${ipInfo?.ip} Country: ${ipInfo?.country}`);
|
|
||||||
|
|
||||||
if (!defaultInterface || !ipInfo) {
|
|
||||||
console.log('Could not get default interface or ip info. Shutting down transmission service');
|
|
||||||
await transmissionContainer.down();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const countryOK = !settings.disallowedCountries.some((country) => ipInfo.country.includes(country));
|
|
||||||
const internalIpOK = !settings.disallowedIntIps.some((ip) => defaultInterface.ip.includes(ip));
|
|
||||||
const externalIpOK = !settings.disallowedExtIps.some((ip) => ipInfo.ip.includes(ip));
|
|
||||||
const interfaceOK = settings.allowedInterfaces.some((inter) => defaultInterface.interface.includes(inter));
|
|
||||||
|
|
||||||
console.log(`Country OK: ${countryOK} Internal IP OK: ${internalIpOK} External IP OK: ${externalIpOK} Interface OK: ${interfaceOK}`);
|
|
||||||
|
|
||||||
if (!countryOK || !internalIpOK || !externalIpOK || !interfaceOK) {
|
|
||||||
console.log('Shutting down transmission service');
|
|
||||||
await transmissionContainer.down();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portOpen) {
|
|
||||||
console.log(`Port ${port} is open`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Port ${port} is not open. Fetching new port.`);
|
|
||||||
|
|
||||||
//fetch new port
|
|
||||||
const request = await fetch(`https://connect.pvdatanet.com/v3/Api/port?ip[]=${defaultInterface.ip}`)
|
|
||||||
const data = await request.json() as {status: string, supported: boolean}
|
|
||||||
|
|
||||||
console.log(`Data received: ${JSON.stringify(data)}`);
|
|
||||||
|
|
||||||
if (!data.status || !data.supported) {
|
|
||||||
console.log('Could not fetch new port. Shutting down transmission service');
|
|
||||||
await transmissionContainer.down();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newPort= Number(data.status.split(" ")[1]);
|
|
||||||
|
|
||||||
if (!newPort) {
|
|
||||||
console.log('Could not parse new port Shutting down transmission service');
|
|
||||||
await transmissionContainer.down();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Setting port to ${newPort}`);
|
|
||||||
await transmissionClient.setPort(newPort);
|
|
||||||
|
|
||||||
console.log('Port set');
|
|
||||||
const newPortOpen = await transmissionClient.testPortIfOpen();
|
|
||||||
console.log(`Port ${newPort} is open: ${newPortOpen}`);
|
|
||||||
|
|
||||||
if (!newPortOpen) {
|
|
||||||
console.log('Port is not open, Shutting down transmission service');
|
|
||||||
await transmissionContainer.down();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { settings } from './settings';
|
||||||
|
|
||||||
|
class NotificationService {
|
||||||
|
async sendNotification(message: string, important = false) {
|
||||||
|
const webhook = settings.discordWebhookUrl;
|
||||||
|
if (!webhook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Posting message to Discord: ${message}`);
|
||||||
|
|
||||||
|
const response = await fetch(webhook, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ content: `${important ? '@here ' :''}${message}` }),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Discord response: ${response.status}, rate limit remaining: ${response.headers.get('x-ratelimit-remaining')}, rate limit reset in: ${response.headers.get('x-ratelimit-reset-after')}s.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notificationService = new NotificationService();
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { Transmission } from './transmission';
|
||||||
|
import { DockerContainer } from './docker';
|
||||||
|
import { settings } from './settings';
|
||||||
|
import { notificationService } from './notification';
|
||||||
|
|
||||||
|
async function downTransmission(transmissionContainer: DockerContainer, reason: string) {
|
||||||
|
console.log(reason);
|
||||||
|
|
||||||
|
await notificationService.sendNotification(reason, true);
|
||||||
|
return await transmissionContainer.down();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setPortFlow() {
|
||||||
|
console.log('Starting set port flow');
|
||||||
|
const transmissionClient = new Transmission(settings.transmission);
|
||||||
|
const transmissionContainer = new DockerContainer(
|
||||||
|
settings.dockerTransmission.service,
|
||||||
|
settings.dockerTransmission.cwd,
|
||||||
|
settings.dockerTransmission.serviceName
|
||||||
|
);
|
||||||
|
|
||||||
|
const containerState = await transmissionContainer.getState();
|
||||||
|
if (containerState?.state !== 'running') {
|
||||||
|
console.log('Transmission service is not running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Gathering info`);
|
||||||
|
const port = await transmissionClient.getPort();
|
||||||
|
console.log(`Port: ${port}`);
|
||||||
|
const portOpen = await transmissionClient.testPortIfOpen();
|
||||||
|
console.log(`Port is open: ${portOpen}`);
|
||||||
|
const defaultInterface = await transmissionContainer.getDefaultInterface();
|
||||||
|
console.log(`Default interface: ${defaultInterface?.interface} Internal ip: ${defaultInterface?.ip}`);
|
||||||
|
const ipInfo = await transmissionContainer.getIpInfo();
|
||||||
|
console.log(`External ip: ${ipInfo?.ip} Country: ${ipInfo?.country}`);
|
||||||
|
|
||||||
|
if (!defaultInterface || !ipInfo) {
|
||||||
|
return await downTransmission(
|
||||||
|
transmissionContainer,
|
||||||
|
`Shutting down: defaultInterface: ${!!defaultInterface} ipInfo: ${!!ipInfo}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const countryOK = !settings.disallowedCountries.some((country) => ipInfo.country.includes(country));
|
||||||
|
const internalIpOK = !settings.disallowedIntIps.some((ip) => defaultInterface.ip.includes(ip));
|
||||||
|
const externalIpOK = !settings.disallowedExtIps.some((ip) => ipInfo.ip.includes(ip));
|
||||||
|
const interfaceOK = settings.allowedInterfaces.some((inter) => defaultInterface.interface.includes(inter));
|
||||||
|
|
||||||
|
console.log(`Country OK: ${countryOK} Internal IP OK: ${internalIpOK} External IP OK: ${externalIpOK} Interface OK: ${interfaceOK}`);
|
||||||
|
|
||||||
|
if (!countryOK || !internalIpOK || !externalIpOK || !interfaceOK) {
|
||||||
|
return await downTransmission(
|
||||||
|
transmissionContainer,
|
||||||
|
`Shutting down: Country OK: ${countryOK}, Internal IP OK: ${internalIpOK}, External IP OK: ${externalIpOK}, Interface OK: ${interfaceOK}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (portOpen) {
|
||||||
|
console.log(`Port ${port} is open`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Port ${port} is not open. Fetching new port.`);
|
||||||
|
|
||||||
|
//fetch new port
|
||||||
|
const request = await fetch(`https://connect.pvdatanet.com/v3/Api/port?ip[]=${defaultInterface.ip}`)
|
||||||
|
const data = await request.json() as {status: string, supported: boolean}
|
||||||
|
|
||||||
|
console.log(`Data received: ${JSON.stringify(data)}`);
|
||||||
|
|
||||||
|
if (!data.status || !data.supported) {
|
||||||
|
return await downTransmission(
|
||||||
|
transmissionContainer,
|
||||||
|
`Shutting down: could not fetch new port, data: ${JSON.stringify(data)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPort= Number(data.status.split(" ")[1]);
|
||||||
|
|
||||||
|
if (!newPort) {
|
||||||
|
return await downTransmission(
|
||||||
|
transmissionContainer,
|
||||||
|
`Shutting down: could not parse new port, data: ${JSON.stringify(data)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Setting port to ${newPort}`);
|
||||||
|
await transmissionClient.setPort(newPort);
|
||||||
|
|
||||||
|
console.log('Port set');
|
||||||
|
const newPortOpen = await transmissionClient.testPortIfOpen();
|
||||||
|
console.log(`Port ${newPort} is open: ${newPortOpen}`);
|
||||||
|
|
||||||
|
if (!newPortOpen) {
|
||||||
|
return await downTransmission(
|
||||||
|
transmissionContainer,
|
||||||
|
`Shutting down: new port is still not open, data: ${JSON.stringify(data)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await notificationService.sendNotification(`Port changed from ${port} to ${newPort} successfully`);
|
||||||
|
}
|
||||||
@@ -17,4 +17,6 @@ export const settings = {
|
|||||||
disallowedCountries: process.env.DISALLOWED_COUNTRIES?.split(',') ?? [],
|
disallowedCountries: process.env.DISALLOWED_COUNTRIES?.split(',') ?? [],
|
||||||
disallowedIntIps: process.env.DISALLOWED_INTERNAL_IPS?.split(',') ?? [],
|
disallowedIntIps: process.env.DISALLOWED_INTERNAL_IPS?.split(',') ?? [],
|
||||||
disallowedExtIps: process.env.DISALLOWED_EXT_IPS?.split(',') ?? [],
|
disallowedExtIps: process.env.DISALLOWED_EXT_IPS?.split(',') ?? [],
|
||||||
|
discordWebhookUrl: process.env.DISCORD_WEBHOOK_URL,
|
||||||
|
updateIntervalMs: (Number(process.env.UPDATE_INTERVAL_SECONDS) ?? 60 * 5) * 1000,
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user