From cc1237ed3c18a4437175248799be52dcdb229b4c Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Mon, 24 Oct 2022 11:26:22 +0000 Subject: [PATCH 1/8] Refactor Host to class & add default path --- src/client.test.ts | 101 ++++++++++++++++++++++----------------------- src/client.ts | 52 +++++++++++------------ src/config.test.ts | 28 +++++++++---- src/config.ts | 42 ++++++++++++------- src/sync.test.ts | 17 ++------ 5 files changed, 125 insertions(+), 115 deletions(-) diff --git a/src/client.test.ts b/src/client.test.ts index 34b373c2..ce13bcfd 100644 --- a/src/client.test.ts +++ b/src/client.test.ts @@ -2,26 +2,23 @@ import { jest } from '@jest/globals'; import nock from 'nock'; import { Blob } from 'node-fetch'; import { Client } from './client'; -import type { Host } from './config'; +import { Host } from './config'; import { Config } from './config'; import { ErrorNotification } from './notify'; describe('Client', () => { - const host: Host = { - baseUrl: 'http://10.0.0.2', - password: 'mypassword' - }; + const host = new Host('http://10.0.0.2', 'mypassword'); const createClient = async () => { - nock(host.baseUrl).get('/admin/index.php?login').reply(200); - nock(host.baseUrl) + nock(host.fullUrl).get('/admin/index.php?login').reply(200); + nock(host.fullUrl) .post('/admin/index.php?login') .reply( 200, '
abcdefgijklmnopqrstuvwxyzabcdefgijklmnopqrst
' ); - return { teleporter: nock(host.baseUrl), client: await Client.create(host) }; + return { teleporter: nock(host.fullUrl), client: await Client.create(host) }; }; beforeEach(() => { @@ -30,8 +27,8 @@ describe('Client', () => { describe('create', () => { test('should throw error if status code is not ok', async () => { - const initialRequest = nock(host.baseUrl).get('/admin/index.php?login').reply(200); - const loginRequest = nock(host.baseUrl).post('/admin/index.php?login').reply(500); + const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); + const loginRequest = nock(host.fullUrl).post('/admin/index.php?login').reply(500); const expectError = expect(Client.create(host)).rejects; @@ -50,8 +47,8 @@ describe('Client', () => { }); test('should throw error if no token is present', async () => { - const initialRequest = nock(host.baseUrl).get('/admin/index.php?login').reply(200); - const loginRequest = nock(host.baseUrl).post('/admin/index.php?login').reply(200); + const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); + const loginRequest = nock(host.fullUrl).post('/admin/index.php?login').reply(200); const expectError = expect(Client.create(host)).rejects; @@ -69,8 +66,8 @@ describe('Client', () => { }); test('should throw error if token is in incorrect format', async () => { - const initialRequest = nock(host.baseUrl).get('/admin/index.php?login').reply(200); - const loginRequest = nock(host.baseUrl) + const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); + const loginRequest = nock(host.fullUrl) .post('/admin/index.php?login') .reply(200, '
abcdef
'); @@ -90,8 +87,8 @@ describe('Client', () => { }); test('should return client', async () => { - const initialRequest = nock(host.baseUrl).get('/admin/index.php?login').reply(200); - const loginRequest = nock(host.baseUrl) + const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); + const loginRequest = nock(host.fullUrl) .post('/admin/index.php?login') .reply( 200, @@ -237,18 +234,18 @@ describe('Client', () => { .reply( 200, 'Processed adlist (14 entries)
\n' + - 'Processed adlist group assignments (13 entries)
\n' + - 'Processed blacklist (exact) (0 entries)
\n' + - 'Processed blacklist (regex) (3 entries)
\n' + - 'Processed client (8 entries)
\n' + - 'Processed client group assignments (16 entries)
\n' + - 'Processed local DNS records (41 entries)
\n' + - 'Processed domain_audit (0 entries)
\n' + - 'Processed black-/whitelist group assignments (10 entries)
\n' + - 'Processed group (3 entries)
\n' + - 'Processed whitelist (exact) (4 entries)
\n' + - 'Processed whitelist (regex) (0 entries)
\n' + - 'OK' + 'Processed adlist group assignments (13 entries)
\n' + + 'Processed blacklist (exact) (0 entries)
\n' + + 'Processed blacklist (regex) (3 entries)
\n' + + 'Processed client (8 entries)
\n' + + 'Processed client group assignments (16 entries)
\n' + + 'Processed local DNS records (41 entries)
\n' + + 'Processed domain_audit (0 entries)
\n' + + 'Processed black-/whitelist group assignments (10 entries)
\n' + + 'Processed group (3 entries)
\n' + + 'Processed whitelist (exact) (4 entries)
\n' + + 'Processed whitelist (regex) (0 entries)
\n' + + 'OK' ); teleporter .get('/admin/scripts/pi-hole/php/gravity.sh.php', undefined) @@ -276,18 +273,18 @@ describe('Client', () => { .reply( 200, 'Processed adlist (14 entries)
\n' + - 'Processed adlist group assignments (13 entries)
\n' + - 'Processed blacklist (exact) (0 entries)
\n' + - 'Processed blacklist (regex) (3 entries)
\n' + - 'Processed client (8 entries)
\n' + - 'Processed client group assignments (16 entries)
\n' + - 'Processed local DNS records (41 entries)
\n' + - 'Processed domain_audit (0 entries)
\n' + - 'Processed black-/whitelist group assignments (10 entries)
\n' + - 'Processed group (3 entries)
\n' + - 'Processed whitelist (exact) (4 entries)
\n' + - 'Processed whitelist (regex) (0 entries)
\n' + - 'OK' + 'Processed adlist group assignments (13 entries)
\n' + + 'Processed blacklist (exact) (0 entries)
\n' + + 'Processed blacklist (regex) (3 entries)
\n' + + 'Processed client (8 entries)
\n' + + 'Processed client group assignments (16 entries)
\n' + + 'Processed local DNS records (41 entries)
\n' + + 'Processed domain_audit (0 entries)
\n' + + 'Processed black-/whitelist group assignments (10 entries)
\n' + + 'Processed group (3 entries)
\n' + + 'Processed whitelist (exact) (4 entries)
\n' + + 'Processed whitelist (regex) (0 entries)
\n' + + 'OK' ); teleporter .get('/admin/scripts/pi-hole/php/gravity.sh.php', undefined) @@ -336,18 +333,18 @@ describe('Client', () => { .reply( 200, 'Processed adlist (14 entries)
\n' + - 'Processed adlist group assignments (13 entries)
\n' + - 'Processed blacklist (exact) (0 entries)
\n' + - 'Processed blacklist (regex) (3 entries)
\n' + - 'Processed client (8 entries)
\n' + - 'Processed client group assignments (16 entries)
\n' + - 'Processed local DNS records (41 entries)
\n' + - 'Processed domain_audit (0 entries)
\n' + - 'Processed black-/whitelist group assignments (10 entries)
\n' + - 'Processed group (3 entries)
\n' + - 'Processed whitelist (exact) (4 entries)
\n' + - 'Processed whitelist (regex) (0 entries)
\n' + - 'OK' + 'Processed adlist group assignments (13 entries)
\n' + + 'Processed blacklist (exact) (0 entries)
\n' + + 'Processed blacklist (regex) (3 entries)
\n' + + 'Processed client (8 entries)
\n' + + 'Processed client group assignments (16 entries)
\n' + + 'Processed local DNS records (41 entries)
\n' + + 'Processed domain_audit (0 entries)
\n' + + 'Processed black-/whitelist group assignments (10 entries)
\n' + + 'Processed group (3 entries)
\n' + + 'Processed whitelist (exact) (4 entries)
\n' + + 'Processed whitelist (regex) (0 entries)
\n' + + 'OK' ); const result = await client.uploadBackup(backup); diff --git a/src/client.ts b/src/client.ts index 5d049094..5448fd95 100644 --- a/src/client.ts +++ b/src/client.ts @@ -18,14 +18,14 @@ export class Client { private fetch: NodeFetchCookie, private host: Host, private token: string - ) {} + ) { } public static async create(host: Host): Promise { - Log.info(chalk.yellow(`➡️ Signing in to ${host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Signing in to ${host.fullUrl}...`)); const fetch = fetchCookie(nodeFetch); - await fetch(`${host.baseUrl}/admin/index.php?login`, { method: 'GET' }); - const response = await fetch(`${host.baseUrl}/admin/index.php?login`, { + await fetch(`${host.fullUrl}/admin/index.php?login`, { method: 'GET' }); + const response = await fetch(`${host.fullUrl}/admin/index.php?login`, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, @@ -34,9 +34,9 @@ export class Client { }); if (response.status !== 200) throw new ErrorNotification({ - message: `There was an error logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, + message: `There was an error logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, verbose: { - host: host.baseUrl, + host: host.fullUrl, status: response.status, responseBody: await response.text() } @@ -44,7 +44,7 @@ export class Client { const token = this.parseResponseForToken(host, await response.text()); - Log.info(chalk.green(`✔️ Successfully signed in to ${host.baseUrl}!`)); + Log.info(chalk.green(`✔️ Successfully signed in to ${host.fullUrl}!`)); return new this(fetch, host, token); } @@ -53,9 +53,9 @@ export class Client { const tokenDiv = root.querySelector('#token'); if (!tokenDiv) throw new ErrorNotification({ - message: `No token could be found while logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, + message: `No token could be found while logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, verbose: { - host: host.baseUrl, + host: host.fullUrl, innerHtml: root.innerHTML } }); @@ -63,9 +63,9 @@ export class Client { const token = tokenDiv.innerText; if (token.length != 44) throw new ErrorNotification({ - message: `A token was found but could not be validated while logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, + message: `A token was found but could not be validated while logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, verbose: { - host: host.baseUrl, + host: host.fullUrl, token: token } }); @@ -74,11 +74,11 @@ export class Client { } public async downloadBackup(): Promise { - Log.info(chalk.yellow(`➡️ Downloading backup from ${this.host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Downloading backup from ${this.host.fullUrl}...`)); const form = this.generateForm(); const response = await this.fetch( - `${this.host.baseUrl}/admin/scripts/pi-hole/php/teleporter.php`, + `${this.host.fullUrl}/admin/scripts/pi-hole/php/teleporter.php`, { body: form, method: 'POST' @@ -89,9 +89,9 @@ export class Client { response.headers.get('content-type') !== 'application/gzip' ) throw new ErrorNotification({ - message: `Failed to download backup from "${this.host.baseUrl}".`, + message: `Failed to download backup from "${this.host.fullUrl}".`, verbose: { - host: this.host.baseUrl, + host: this.host.fullUrl, status: response.status, responseBody: await response.text() } @@ -99,19 +99,19 @@ export class Client { const data = await response.arrayBuffer(); - Log.info(chalk.green(`✔️ Backup from ${this.host.baseUrl} completed!`)); + Log.info(chalk.green(`✔️ Backup from ${this.host.fullUrl} completed!`)); return new Blob([data]); } public async uploadBackup(backup: Blob): Promise { - Log.info(chalk.yellow(`➡️ Uploading backup to ${this.host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Uploading backup to ${this.host.fullUrl}...`)); const form = this.generateForm(); form.append('action', 'in'); form.append('zip_file', backup, 'backup.tar.gz'); const uploadResponse = await this.fetch( - `${this.host.baseUrl}/admin/scripts/pi-hole/php/teleporter.php`, + `${this.host.fullUrl}/admin/scripts/pi-hole/php/teleporter.php`, { body: form, method: 'POST' @@ -120,21 +120,21 @@ export class Client { const uploadText = await uploadResponse.text(); if (uploadResponse.status !== 200 || !uploadText.endsWith('OK')) throw new ErrorNotification({ - message: `Failed to upload backup to "${this.host.baseUrl}".`, + message: `Failed to upload backup to "${this.host.fullUrl}".`, verbose: { - host: this.host.baseUrl, + host: this.host.fullUrl, status: uploadResponse.status, responseBody: uploadText } }); - Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.baseUrl}!`)); + Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.fullUrl}!`)); Log.verbose(`Result:\n${chalk.blue(uploadText)}`); if (Config.updateGravity) { - Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.fullUrl}...`)); const gravityUpdateResponse = await this.fetch( - `${this.host.baseUrl}/admin/scripts/pi-hole/php/gravity.sh.php`, + `${this.host.fullUrl}/admin/scripts/pi-hole/php/gravity.sh.php`, { method: 'GET' } ); @@ -146,15 +146,15 @@ export class Client { !updateText.endsWith('Pi-hole blocking is enabled') ) throw new ErrorNotification({ - message: `Failed updating gravity on "${this.host.baseUrl}".`, + message: `Failed updating gravity on "${this.host.fullUrl}".`, verbose: { - host: this.host.baseUrl, + host: this.host.fullUrl, status: gravityUpdateResponse.status, eventStream: updateText } }); - Log.info(chalk.green(`✔️ Gravity updated on ${this.host.baseUrl}!`)); + Log.info(chalk.green(`✔️ Gravity updated on ${this.host.fullUrl}!`)); Log.verbose(`Result:\n${chalk.blue(updateText)}`); } diff --git a/src/config.test.ts b/src/config.test.ts index 9295877f..218ebe4a 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -91,12 +91,14 @@ describe('Config', () => { const expected = { baseUrl: 'http://10.0.0.2', + fullUrl: 'http://10.0.0.2', + path: '', password: 'mypassword' }; - expect(Config.primaryHost).toStrictEqual(expected); + expect(Config.primaryHost).toEqual(expected); resetEnv(); - expect(Config.primaryHost).toStrictEqual(expected); + expect(Config.primaryHost).toEqual(expected); }); }); @@ -132,13 +134,15 @@ describe('Config', () => { const expected = [ { baseUrl: 'http://10.0.0.3', - password: 'mypassword' + password: 'mypassword', + fullUrl: 'http://10.0.0.3', + path: '' } ]; - expect(Config.secondaryHosts).toStrictEqual(expected); + expect(Config.secondaryHosts).toEqual(expected); resetEnv(); - expect(Config.secondaryHosts).toStrictEqual(expected); + expect(Config.secondaryHosts).toEqual(expected); }); test('should return multiple secondary hosts', () => { @@ -151,18 +155,24 @@ describe('Config', () => { process.env['SECONDARY_HOST_5_BASE_URL'] = 'http://10.0.0.7'; process.env['SECONDARY_HOST_5_PASSWORD'] = 'mypassword4'; - expect(Config.secondaryHosts).toStrictEqual([ + expect(Config.secondaryHosts).toEqual([ { baseUrl: 'http://10.0.0.3', - password: 'mypassword1' + password: 'mypassword1', + fullUrl: 'http://10.0.0.3', + path: '' }, { baseUrl: 'http://10.0.0.4', - password: 'mypassword2' + password: 'mypassword2', + fullUrl: 'http://10.0.0.4', + path: '' }, { baseUrl: 'http://10.0.0.5', - password: 'mypassword3' + password: 'mypassword3', + fullUrl: 'http://10.0.0.5', + path: '' } ]); }); diff --git a/src/config.ts b/src/config.ts index de45ad16..8421fda5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,10 +7,10 @@ export class Config { private static _intervalMinutes?: number; static get primaryHost(): Host { - this._primaryHost ??= { - baseUrl: this.getRequiredEnv('PRIMARY_HOST_BASE_URL'), - password: this.getRequiredEnv('PRIMARY_HOST_PASSWORD') - }; + this._primaryHost ??= new Host( + this.getRequiredEnv('PRIMARY_HOST_BASE_URL'), + this.getRequiredEnv('PRIMARY_HOST_PASSWORD') + ); return this._primaryHost; } @@ -18,10 +18,10 @@ export class Config { static get secondaryHosts(): Host[] { if (!this._secondaryHosts) { this._secondaryHosts = [ - { - baseUrl: this.getRequiredEnv('SECONDARY_HOST_1_BASE_URL'), - password: this.getRequiredEnv('SECONDARY_HOST_1_PASSWORD') - } + new Host( + this.getRequiredEnv('SECONDARY_HOST_1_BASE_URL'), + this.getRequiredEnv('SECONDARY_HOST_1_PASSWORD') + ) ]; let count = 2; @@ -29,10 +29,11 @@ export class Config { process.env[`SECONDARY_HOST_${count}_BASE_URL`] !== undefined && process.env[`SECONDARY_HOST_${count}_PASSWORD`] !== undefined ) { - this._secondaryHosts.push({ - baseUrl: process.env[`SECONDARY_HOST_${count}_BASE_URL`]!, - password: process.env[`SECONDARY_HOST_${count}_PASSWORD`]! - }); + this._secondaryHosts.push( + new Host( + this.getRequiredEnv(`SECONDARY_HOST_${count}_BASE_URL`), + this.getRequiredEnv(`SECONDARY_HOST_${count}_PASSWORD`) + )); count++; } @@ -42,7 +43,7 @@ export class Config { } static get allHostBaseUrls(): string[] { - return [this.primaryHost, ...this.secondaryHosts].map((host) => host.baseUrl); + return [this.primaryHost, ...this.secondaryHosts].map((host) => host.fullUrl); } static get syncOptions(): SyncOptions { @@ -160,7 +161,18 @@ export interface SyncOptions { flushtables: boolean; } -export interface Host { - baseUrl: string; +export class Host { + private baseUrl: string; + private path: string; + fullUrl: string; password: string; + + constructor(baseUrl: string, password: string, path?: string) { + this.path = path ?? ''; + this.baseUrl = baseUrl; + this.password = password + this.fullUrl = this.baseUrl + this.path + } } + + diff --git a/src/sync.test.ts b/src/sync.test.ts index 4a361157..29d8a0c5 100644 --- a/src/sync.test.ts +++ b/src/sync.test.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; import nock from 'nock'; import { Blob } from 'node-fetch'; import { Client } from './client'; -import { Config } from './config'; +import { Config, Host } from './config'; import { Log } from './log'; import { ErrorNotification, Notify } from './notify'; import { Sync } from './sync'; @@ -20,19 +20,10 @@ describe('entrypoint', () => { let secondaryHostClient1: Client; let secondaryHostClient2: Client; - const primaryHostValue = { - baseUrl: 'http://10.0.0.2', - password: 'password1' - }; + const primaryHostValue = new Host('http://10.0.0.2', 'password1'); const secondaryHostsValue = [ - { - baseUrl: 'http://10.0.0.3', - password: 'password2' - }, - { - baseUrl: 'http://10.0.0.4', - password: 'password3' - } + new Host('http://10.0.0.3', 'password2'), + new Host('http://10.0.0.4', 'password3') ]; const backupData = new Blob([]); From 58036dc8ed5a009951936a3ff227c0ea6480b692 Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Mon, 24 Oct 2022 11:58:14 +0000 Subject: [PATCH 2/8] remove static /admin path from all requests and set default --- src/client.test.ts | 40 ++++++++++++++++----------------- src/client.ts | 56 +++++++++++++++++++++++++--------------------- src/config.test.ts | 28 +++++++++++------------ src/config.ts | 9 ++++---- src/notify.test.ts | 6 ++--- src/notify.ts | 2 +- 6 files changed, 74 insertions(+), 67 deletions(-) diff --git a/src/client.test.ts b/src/client.test.ts index ce13bcfd..ee0bdf34 100644 --- a/src/client.test.ts +++ b/src/client.test.ts @@ -10,9 +10,9 @@ describe('Client', () => { const host = new Host('http://10.0.0.2', 'mypassword'); const createClient = async () => { - nock(host.fullUrl).get('/admin/index.php?login').reply(200); + nock(host.fullUrl).get('/index.php?login').reply(200); nock(host.fullUrl) - .post('/admin/index.php?login') + .post('/index.php?login') .reply( 200, '
abcdefgijklmnopqrstuvwxyzabcdefgijklmnopqrst
' @@ -27,8 +27,8 @@ describe('Client', () => { describe('create', () => { test('should throw error if status code is not ok', async () => { - const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); - const loginRequest = nock(host.fullUrl).post('/admin/index.php?login').reply(500); + const initialRequest = nock(host.fullUrl).get('/index.php?login').reply(200); + const loginRequest = nock(host.fullUrl).post('/index.php?login').reply(500); const expectError = expect(Client.create(host)).rejects; @@ -47,8 +47,8 @@ describe('Client', () => { }); test('should throw error if no token is present', async () => { - const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); - const loginRequest = nock(host.fullUrl).post('/admin/index.php?login').reply(200); + const initialRequest = nock(host.fullUrl).get('/index.php?login').reply(200); + const loginRequest = nock(host.fullUrl).post('/index.php?login').reply(200); const expectError = expect(Client.create(host)).rejects; @@ -66,9 +66,9 @@ describe('Client', () => { }); test('should throw error if token is in incorrect format', async () => { - const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); + const initialRequest = nock(host.fullUrl).get('/index.php?login').reply(200); const loginRequest = nock(host.fullUrl) - .post('/admin/index.php?login') + .post('/index.php?login') .reply(200, '
abcdef
'); const expectError = expect(Client.create(host)).rejects; @@ -87,9 +87,9 @@ describe('Client', () => { }); test('should return client', async () => { - const initialRequest = nock(host.fullUrl).get('/admin/index.php?login').reply(200); + const initialRequest = nock(host.fullUrl).get('/index.php?login').reply(200); const loginRequest = nock(host.fullUrl) - .post('/admin/index.php?login') + .post('/index.php?login') .reply( 200, '
abcdefgijklmnopqrstuvwxyzabcdefgijklmnopqrst
' @@ -115,7 +115,7 @@ describe('Client', () => { }); test('should throw error if response is non-200', async () => { - teleporter.post('/admin/scripts/pi-hole/php/teleporter.php').reply(500); + teleporter.post('/scripts/pi-hole/php/teleporter.php').reply(500); const expectError = expect(client.downloadBackup()).rejects; @@ -132,7 +132,7 @@ describe('Client', () => { test('should throw error if content type is not gzip', async () => { teleporter - .post('/admin/scripts/pi-hole/php/teleporter.php') + .post('/scripts/pi-hole/php/teleporter.php') .reply(200, undefined, { 'content-type': 'text/html' }); const expectError = expect(client.downloadBackup()).rejects; @@ -153,7 +153,7 @@ describe('Client', () => { let requestBody = ''; teleporter - .post('/admin/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body)) + .post('/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body)) .reply(200, undefined, { 'content-type': 'application/gzip' }); const backup = await client.downloadBackup(); @@ -197,7 +197,7 @@ describe('Client', () => { }); test('should throw error if response is non-200', async () => { - teleporter.post('/admin/scripts/pi-hole/php/teleporter.php').reply(500); + teleporter.post('/scripts/pi-hole/php/teleporter.php').reply(500); const expectError = expect(client.uploadBackup(backup)).rejects; @@ -213,7 +213,7 @@ describe('Client', () => { }); test('should throw error if response does not end with "OK"', async () => { - teleporter.post('/admin/scripts/pi-hole/php/teleporter.php').reply(200); + teleporter.post('/scripts/pi-hole/php/teleporter.php').reply(200); const expectError = expect(client.uploadBackup(backup)).rejects; @@ -230,7 +230,7 @@ describe('Client', () => { test('should throw error if gravity update fails', async () => { teleporter - .post('/admin/scripts/pi-hole/php/teleporter.php') + .post('/scripts/pi-hole/php/teleporter.php') .reply( 200, 'Processed adlist (14 entries)
\n' + @@ -248,7 +248,7 @@ describe('Client', () => { 'OK' ); teleporter - .get('/admin/scripts/pi-hole/php/gravity.sh.php', undefined) + .get('/scripts/pi-hole/php/gravity.sh.php', undefined) .reply(200, '\ndata: \n\ndata: [✓] TCP (IPv6)\ndata: \ndata: \n\ndata:'); const expectError = expect(client.uploadBackup(backup)).rejects; @@ -269,7 +269,7 @@ describe('Client', () => { let requestBody = ''; teleporter - .post('/admin/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body)) + .post('/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body)) .reply( 200, 'Processed adlist (14 entries)
\n' + @@ -287,7 +287,7 @@ describe('Client', () => { 'OK' ); teleporter - .get('/admin/scripts/pi-hole/php/gravity.sh.php', undefined) + .get('/scripts/pi-hole/php/gravity.sh.php', undefined) .reply( 200, '\ndata: \n\ndata: [✓] TCP (IPv6)\ndata: \ndata: \n\ndata: [✓] Pi-hole blocking is enabled\ndata: \n\ndata:' @@ -329,7 +329,7 @@ describe('Client', () => { let requestBody = ''; teleporter - .post('/admin/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body)) + .post('/scripts/pi-hole/php/teleporter.php', (body) => (requestBody = body)) .reply( 200, 'Processed adlist (14 entries)
\n' + diff --git a/src/client.ts b/src/client.ts index 5448fd95..efe62d5a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -21,11 +21,11 @@ export class Client { ) { } public static async create(host: Host): Promise { - Log.info(chalk.yellow(`➡️ Signing in to ${host.fullUrl}...`)); + Log.info(chalk.yellow(`➡️ Signing in to ${host.baseUrl}...`)); const fetch = fetchCookie(nodeFetch); - await fetch(`${host.fullUrl}/admin/index.php?login`, { method: 'GET' }); - const response = await fetch(`${host.fullUrl}/admin/index.php?login`, { + await fetch(`${host.fullUrl}/index.php?login`, { method: 'GET' }); + const response = await fetch(`${host.fullUrl}/index.php?login`, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, @@ -34,9 +34,10 @@ export class Client { }); if (response.status !== 200) throw new ErrorNotification({ - message: `There was an error logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, + message: `There was an error logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, verbose: { - host: host.fullUrl, + host: host.baseUrl, + path: host.path, status: response.status, responseBody: await response.text() } @@ -44,7 +45,7 @@ export class Client { const token = this.parseResponseForToken(host, await response.text()); - Log.info(chalk.green(`✔️ Successfully signed in to ${host.fullUrl}!`)); + Log.info(chalk.green(`✔️ Successfully signed in to ${host.baseUrl}!`)); return new this(fetch, host, token); } @@ -53,9 +54,10 @@ export class Client { const tokenDiv = root.querySelector('#token'); if (!tokenDiv) throw new ErrorNotification({ - message: `No token could be found while logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, + message: `No token could be found while logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, verbose: { - host: host.fullUrl, + host: host.baseUrl, + path: host.path, innerHtml: root.innerHTML } }); @@ -63,9 +65,10 @@ export class Client { const token = tokenDiv.innerText; if (token.length != 44) throw new ErrorNotification({ - message: `A token was found but could not be validated while logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, + message: `A token was found but could not be validated while logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, verbose: { - host: host.fullUrl, + host: host.baseUrl, + path: host.path, token: token } }); @@ -74,11 +77,11 @@ export class Client { } public async downloadBackup(): Promise { - Log.info(chalk.yellow(`➡️ Downloading backup from ${this.host.fullUrl}...`)); + Log.info(chalk.yellow(`➡️ Downloading backup from ${this.host.baseUrl}...`)); const form = this.generateForm(); const response = await this.fetch( - `${this.host.fullUrl}/admin/scripts/pi-hole/php/teleporter.php`, + `${this.host.fullUrl}/scripts/pi-hole/php/teleporter.php`, { body: form, method: 'POST' @@ -89,9 +92,10 @@ export class Client { response.headers.get('content-type') !== 'application/gzip' ) throw new ErrorNotification({ - message: `Failed to download backup from "${this.host.fullUrl}".`, + message: `Failed to download backup from "${this.host.baseUrl}".`, verbose: { - host: this.host.fullUrl, + host: this.host.baseUrl, + path: this.host.path, status: response.status, responseBody: await response.text() } @@ -99,19 +103,19 @@ export class Client { const data = await response.arrayBuffer(); - Log.info(chalk.green(`✔️ Backup from ${this.host.fullUrl} completed!`)); + Log.info(chalk.green(`✔️ Backup from ${this.host.baseUrl} completed!`)); return new Blob([data]); } public async uploadBackup(backup: Blob): Promise { - Log.info(chalk.yellow(`➡️ Uploading backup to ${this.host.fullUrl}...`)); + Log.info(chalk.yellow(`➡️ Uploading backup to ${this.host.baseUrl}...`)); const form = this.generateForm(); form.append('action', 'in'); form.append('zip_file', backup, 'backup.tar.gz'); const uploadResponse = await this.fetch( - `${this.host.fullUrl}/admin/scripts/pi-hole/php/teleporter.php`, + `${this.host.fullUrl}/scripts/pi-hole/php/teleporter.php`, { body: form, method: 'POST' @@ -120,21 +124,22 @@ export class Client { const uploadText = await uploadResponse.text(); if (uploadResponse.status !== 200 || !uploadText.endsWith('OK')) throw new ErrorNotification({ - message: `Failed to upload backup to "${this.host.fullUrl}".`, + message: `Failed to upload backup to "${this.host.baseUrl}".`, verbose: { - host: this.host.fullUrl, + host: this.host.baseUrl, + path: this.host.path, status: uploadResponse.status, responseBody: uploadText } }); - Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.fullUrl}!`)); + Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.baseUrl}!`)); Log.verbose(`Result:\n${chalk.blue(uploadText)}`); if (Config.updateGravity) { - Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.fullUrl}...`)); + Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.baseUrl}...`)); const gravityUpdateResponse = await this.fetch( - `${this.host.fullUrl}/admin/scripts/pi-hole/php/gravity.sh.php`, + `${this.host.fullUrl}/scripts/pi-hole/php/gravity.sh.php`, { method: 'GET' } ); @@ -146,15 +151,16 @@ export class Client { !updateText.endsWith('Pi-hole blocking is enabled') ) throw new ErrorNotification({ - message: `Failed updating gravity on "${this.host.fullUrl}".`, + message: `Failed updating gravity on "${this.host.baseUrl}".`, verbose: { - host: this.host.fullUrl, + host: this.host.baseUrl, + path: this.host.path, status: gravityUpdateResponse.status, eventStream: updateText } }); - Log.info(chalk.green(`✔️ Gravity updated on ${this.host.fullUrl}!`)); + Log.info(chalk.green(`✔️ Gravity updated on ${this.host.baseUrl}!`)); Log.verbose(`Result:\n${chalk.blue(updateText)}`); } diff --git a/src/config.test.ts b/src/config.test.ts index 218ebe4a..199ed6ca 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -91,8 +91,8 @@ describe('Config', () => { const expected = { baseUrl: 'http://10.0.0.2', - fullUrl: 'http://10.0.0.2', - path: '', + fullUrl: 'http://10.0.0.2/admin', + path: '/admin', password: 'mypassword' }; @@ -135,8 +135,8 @@ describe('Config', () => { { baseUrl: 'http://10.0.0.3', password: 'mypassword', - fullUrl: 'http://10.0.0.3', - path: '' + fullUrl: 'http://10.0.0.3/admin', + path: '/admin' } ]; @@ -159,20 +159,20 @@ describe('Config', () => { { baseUrl: 'http://10.0.0.3', password: 'mypassword1', - fullUrl: 'http://10.0.0.3', - path: '' + fullUrl: 'http://10.0.0.3/admin', + path: '/admin' }, { baseUrl: 'http://10.0.0.4', password: 'mypassword2', - fullUrl: 'http://10.0.0.4', - path: '' + fullUrl: 'http://10.0.0.4/admin', + path: '/admin' }, { baseUrl: 'http://10.0.0.5', password: 'mypassword3', - fullUrl: 'http://10.0.0.5', - path: '' + fullUrl: 'http://10.0.0.5/admin', + path: '/admin' } ]); }); @@ -187,10 +187,10 @@ describe('Config', () => { process.env['SECONDARY_HOST_2_BASE_URL'] = 'http://10.0.0.4'; process.env['SECONDARY_HOST_2_PASSWORD'] = 'mypassword3'; - expect(Config.allHostBaseUrls).toStrictEqual([ - 'http://10.0.0.2', - 'http://10.0.0.3', - 'http://10.0.0.4' + expect(Config.allHostUrls).toStrictEqual([ + 'http://10.0.0.2/admin', + 'http://10.0.0.3/admin', + 'http://10.0.0.4/admin' ]); }); }); diff --git a/src/config.ts b/src/config.ts index 8421fda5..3dfd4c61 100644 --- a/src/config.ts +++ b/src/config.ts @@ -42,7 +42,8 @@ export class Config { return this._secondaryHosts; } - static get allHostBaseUrls(): string[] { + //TODO look at usages + static get allHostUrls(): string[] { return [this.primaryHost, ...this.secondaryHosts].map((host) => host.fullUrl); } @@ -162,13 +163,13 @@ export interface SyncOptions { } export class Host { - private baseUrl: string; - private path: string; + baseUrl: string; + path: string; fullUrl: string; password: string; constructor(baseUrl: string, password: string, path?: string) { - this.path = path ?? ''; + this.path = path ?? '/admin'; this.baseUrl = baseUrl; this.password = password this.fullUrl = this.baseUrl + this.path diff --git a/src/notify.test.ts b/src/notify.test.ts index 4fe27f7c..24c6a979 100644 --- a/src/notify.test.ts +++ b/src/notify.test.ts @@ -220,8 +220,8 @@ describe('Notify', () => { test('should Notify on throw of connection refused FetchError', async () => { notifyOfFailure = jest.spyOn(Notify, 'ofFailure'); const allHostBaseUrls = jest - .spyOn(Config, 'allHostBaseUrls', 'get') - .mockReturnValue(['http://10.0.0.2', 'http://10.0.0.3']); + .spyOn(Config, 'allHostUrls', 'get') + .mockReturnValue(['http://10.0.0.2/admin', 'http://10.0.0.3/admin']); await Notify.ofThrow( new FetchError( @@ -235,7 +235,7 @@ describe('Notify', () => { expect(notifyOfFailure).toHaveBeenCalledTimes(1); expect(notifyOfFailure).toHaveBeenCalledWith( expect.objectContaining({ - message: 'The host "http://10.0.0.3" refused to connect. Is it down?', + message: 'The host "http://10.0.0.3/admin" refused to connect. Is it down?', verbose: 'request to http://10.0.0.3/admin/index.php?login failed, reason: connect ECONNREFUSED 10.0.0.2:443' }) diff --git a/src/notify.ts b/src/notify.ts index 061376f3..058312a4 100644 --- a/src/notify.ts +++ b/src/notify.ts @@ -15,7 +15,7 @@ export class Notify { else Notify.queueError(error); } else if (error instanceof FetchError && error.code === 'ECONNREFUSED') { const messageSubstring = error.message.split('ECONNREFUSED')[0]!; - const url = Config.allHostBaseUrls.find((url) => messageSubstring.includes(url)); + const url = Config.allHostUrls.find((url) => messageSubstring.includes(url)); await Notify.ofThrow( new ErrorNotification({ From 06492e512f2d94313e622987558c464e72667643 Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Mon, 24 Oct 2022 12:50:51 +0000 Subject: [PATCH 3/8] Process paths from env, add tests --- src/client.test.ts | 80 +++++++++++++++++++++++++--------------------- src/client.ts | 2 +- src/config.test.ts | 78 ++++++++++++++++++++++++++++++++++++++++---- src/config.ts | 30 +++++++++++------ 4 files changed, 137 insertions(+), 53 deletions(-) diff --git a/src/client.test.ts b/src/client.test.ts index ee0bdf34..a200ff81 100644 --- a/src/client.test.ts +++ b/src/client.test.ts @@ -38,6 +38,7 @@ describe('Client', () => { 'There was an error logging in to "http://10.0.0.2" - are you able to log in with the configured password?', verbose: { host: 'http://10.0.0.2', + path: '/admin', status: 500, responseBody: '' } @@ -58,6 +59,7 @@ describe('Client', () => { 'No token could be found while logging in to "http://10.0.0.2" - are you able to log in with the configured password?', verbose: { host: 'http://10.0.0.2', + path: '/admin', innerHtml: '' } }); @@ -79,6 +81,7 @@ describe('Client', () => { 'A token was found but could not be validated while logging in to "http://10.0.0.2" - are you able to log in with the configured password?', verbose: { host: 'http://10.0.0.2', + path: '/admin', token: 'abcdef' } }); @@ -124,6 +127,7 @@ describe('Client', () => { message: 'Failed to download backup from "http://10.0.0.2".', verbose: { host: 'http://10.0.0.2', + path: '/admin', status: 500, responseBody: '' } @@ -142,6 +146,7 @@ describe('Client', () => { message: 'Failed to download backup from "http://10.0.0.2".', verbose: { host: 'http://10.0.0.2', + path: '/admin', status: 200, responseBody: '' } @@ -206,6 +211,7 @@ describe('Client', () => { message: 'Failed to upload backup to "http://10.0.0.2".', verbose: { host: 'http://10.0.0.2', + path: '/admin', status: 500, responseBody: '' } @@ -222,6 +228,7 @@ describe('Client', () => { message: 'Failed to upload backup to "http://10.0.0.2".', verbose: { host: 'http://10.0.0.2', + path: '/admin', status: 200, responseBody: '' } @@ -234,18 +241,18 @@ describe('Client', () => { .reply( 200, 'Processed adlist (14 entries)
\n' + - 'Processed adlist group assignments (13 entries)
\n' + - 'Processed blacklist (exact) (0 entries)
\n' + - 'Processed blacklist (regex) (3 entries)
\n' + - 'Processed client (8 entries)
\n' + - 'Processed client group assignments (16 entries)
\n' + - 'Processed local DNS records (41 entries)
\n' + - 'Processed domain_audit (0 entries)
\n' + - 'Processed black-/whitelist group assignments (10 entries)
\n' + - 'Processed group (3 entries)
\n' + - 'Processed whitelist (exact) (4 entries)
\n' + - 'Processed whitelist (regex) (0 entries)
\n' + - 'OK' + 'Processed adlist group assignments (13 entries)
\n' + + 'Processed blacklist (exact) (0 entries)
\n' + + 'Processed blacklist (regex) (3 entries)
\n' + + 'Processed client (8 entries)
\n' + + 'Processed client group assignments (16 entries)
\n' + + 'Processed local DNS records (41 entries)
\n' + + 'Processed domain_audit (0 entries)
\n' + + 'Processed black-/whitelist group assignments (10 entries)
\n' + + 'Processed group (3 entries)
\n' + + 'Processed whitelist (exact) (4 entries)
\n' + + 'Processed whitelist (regex) (0 entries)
\n' + + 'OK' ); teleporter .get('/scripts/pi-hole/php/gravity.sh.php', undefined) @@ -258,6 +265,7 @@ describe('Client', () => { message: 'Failed updating gravity on "http://10.0.0.2".', verbose: { host: 'http://10.0.0.2', + path: '/admin', status: 200, eventStream: '[✓] TCP (IPv6)' } @@ -273,18 +281,18 @@ describe('Client', () => { .reply( 200, 'Processed adlist (14 entries)
\n' + - 'Processed adlist group assignments (13 entries)
\n' + - 'Processed blacklist (exact) (0 entries)
\n' + - 'Processed blacklist (regex) (3 entries)
\n' + - 'Processed client (8 entries)
\n' + - 'Processed client group assignments (16 entries)
\n' + - 'Processed local DNS records (41 entries)
\n' + - 'Processed domain_audit (0 entries)
\n' + - 'Processed black-/whitelist group assignments (10 entries)
\n' + - 'Processed group (3 entries)
\n' + - 'Processed whitelist (exact) (4 entries)
\n' + - 'Processed whitelist (regex) (0 entries)
\n' + - 'OK' + 'Processed adlist group assignments (13 entries)
\n' + + 'Processed blacklist (exact) (0 entries)
\n' + + 'Processed blacklist (regex) (3 entries)
\n' + + 'Processed client (8 entries)
\n' + + 'Processed client group assignments (16 entries)
\n' + + 'Processed local DNS records (41 entries)
\n' + + 'Processed domain_audit (0 entries)
\n' + + 'Processed black-/whitelist group assignments (10 entries)
\n' + + 'Processed group (3 entries)
\n' + + 'Processed whitelist (exact) (4 entries)
\n' + + 'Processed whitelist (regex) (0 entries)
\n' + + 'OK' ); teleporter .get('/scripts/pi-hole/php/gravity.sh.php', undefined) @@ -333,18 +341,18 @@ describe('Client', () => { .reply( 200, 'Processed adlist (14 entries)
\n' + - 'Processed adlist group assignments (13 entries)
\n' + - 'Processed blacklist (exact) (0 entries)
\n' + - 'Processed blacklist (regex) (3 entries)
\n' + - 'Processed client (8 entries)
\n' + - 'Processed client group assignments (16 entries)
\n' + - 'Processed local DNS records (41 entries)
\n' + - 'Processed domain_audit (0 entries)
\n' + - 'Processed black-/whitelist group assignments (10 entries)
\n' + - 'Processed group (3 entries)
\n' + - 'Processed whitelist (exact) (4 entries)
\n' + - 'Processed whitelist (regex) (0 entries)
\n' + - 'OK' + 'Processed adlist group assignments (13 entries)
\n' + + 'Processed blacklist (exact) (0 entries)
\n' + + 'Processed blacklist (regex) (3 entries)
\n' + + 'Processed client (8 entries)
\n' + + 'Processed client group assignments (16 entries)
\n' + + 'Processed local DNS records (41 entries)
\n' + + 'Processed domain_audit (0 entries)
\n' + + 'Processed black-/whitelist group assignments (10 entries)
\n' + + 'Processed group (3 entries)
\n' + + 'Processed whitelist (exact) (4 entries)
\n' + + 'Processed whitelist (regex) (0 entries)
\n' + + 'OK' ); const result = await client.uploadBackup(backup); diff --git a/src/client.ts b/src/client.ts index efe62d5a..b8215cda 100644 --- a/src/client.ts +++ b/src/client.ts @@ -18,7 +18,7 @@ export class Client { private fetch: NodeFetchCookie, private host: Host, private token: string - ) { } + ) {} public static async create(host: Host): Promise { Log.info(chalk.yellow(`➡️ Signing in to ${host.baseUrl}...`)); diff --git a/src/config.test.ts b/src/config.test.ts index 199ed6ca..12b7cc9a 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -102,6 +102,67 @@ describe('Config', () => { }); }); + describe('paths', () => { + test('should add default /admin path when not provided', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + + const expected = 'http://10.0.0.2/admin'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); + + test('should add user defined path', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = '/mypath'; + + const expected = 'http://10.0.0.2/mypath'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); + + test('should trim trailing slash', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = '/mypath/'; + + const expected = 'http://10.0.0.2/mypath'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); + + test('should allow empty path', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = ''; + + const expected = 'http://10.0.0.2'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); + + test('should allow empty path from slash', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = '/'; + + const expected = 'http://10.0.0.2'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); + + test('should add preceeding slash if ommitted', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = 'mypath'; + + const expected = 'http://10.0.0.2/mypath'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); + }); + describe('secondaryHosts', () => { test('should error and exit if "SECONDARY_HOST_1_BASE_URL" is undefined', () => { process.env['SECONDARY_HOST_1_PASSWORD'] = 'mypassword'; @@ -148,31 +209,34 @@ describe('Config', () => { test('should return multiple secondary hosts', () => { process.env['SECONDARY_HOST_1_BASE_URL'] = 'http://10.0.0.3'; process.env['SECONDARY_HOST_1_PASSWORD'] = 'mypassword1'; + process.env['SECONDARY_HOST_1_PATH'] = '/mypath'; process.env['SECONDARY_HOST_2_BASE_URL'] = 'http://10.0.0.4'; process.env['SECONDARY_HOST_2_PASSWORD'] = 'mypassword2'; + process.env['SECONDARY_HOST_2_PATH'] = '/mypath'; process.env['SECONDARY_HOST_3_BASE_URL'] = 'http://10.0.0.5'; process.env['SECONDARY_HOST_3_PASSWORD'] = 'mypassword3'; + process.env['SECONDARY_HOST_3_PATH'] = '/mypath'; process.env['SECONDARY_HOST_5_BASE_URL'] = 'http://10.0.0.7'; - process.env['SECONDARY_HOST_5_PASSWORD'] = 'mypassword4'; + process.env['SECONDARY_HOST_5_PATH'] = '/mypath'; expect(Config.secondaryHosts).toEqual([ { baseUrl: 'http://10.0.0.3', password: 'mypassword1', - fullUrl: 'http://10.0.0.3/admin', - path: '/admin' + fullUrl: 'http://10.0.0.3/mypath', + path: '/mypath' }, { baseUrl: 'http://10.0.0.4', password: 'mypassword2', - fullUrl: 'http://10.0.0.4/admin', - path: '/admin' + fullUrl: 'http://10.0.0.4/mypath', + path: '/mypath' }, { baseUrl: 'http://10.0.0.5', password: 'mypassword3', - fullUrl: 'http://10.0.0.5/admin', - path: '/admin' + fullUrl: 'http://10.0.0.5/mypath', + path: '/mypath' } ]); }); diff --git a/src/config.ts b/src/config.ts index 3dfd4c61..b73d4191 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,7 +9,8 @@ export class Config { static get primaryHost(): Host { this._primaryHost ??= new Host( this.getRequiredEnv('PRIMARY_HOST_BASE_URL'), - this.getRequiredEnv('PRIMARY_HOST_PASSWORD') + this.getRequiredEnv('PRIMARY_HOST_PASSWORD'), + process.env['PRIMARY_HOST_PATH'] ); return this._primaryHost; @@ -20,7 +21,8 @@ export class Config { this._secondaryHosts = [ new Host( this.getRequiredEnv('SECONDARY_HOST_1_BASE_URL'), - this.getRequiredEnv('SECONDARY_HOST_1_PASSWORD') + this.getRequiredEnv('SECONDARY_HOST_1_PASSWORD'), + process.env['SECONDARY_HOST_1_PATH'] ) ]; @@ -32,8 +34,10 @@ export class Config { this._secondaryHosts.push( new Host( this.getRequiredEnv(`SECONDARY_HOST_${count}_BASE_URL`), - this.getRequiredEnv(`SECONDARY_HOST_${count}_PASSWORD`) - )); + this.getRequiredEnv(`SECONDARY_HOST_${count}_PASSWORD`), + process.env[`SECONDARY_HOST_${count}_PATH`] + ) + ); count++; } @@ -169,11 +173,19 @@ export class Host { password: string; constructor(baseUrl: string, password: string, path?: string) { - this.path = path ?? '/admin'; this.baseUrl = baseUrl; - this.password = password - this.fullUrl = this.baseUrl + this.path - } -} + this.password = password; + + if (path && path.endsWith('/')) { + path = path.slice(0, path.length - 1); + } + if (path && !path.startsWith('/')) { + path = '/' + path; + } + + this.path = path ?? '/admin'; + this.fullUrl = this.baseUrl + this.path; + } +} From 39a899d4d23fefe5fd9d51d8a3ebcfced5a0dffe Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Mon, 24 Oct 2022 13:15:24 +0000 Subject: [PATCH 4/8] Extra test for compatibility --- src/config.test.ts | 11 +++++++++++ src/config.ts | 13 ++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index 12b7cc9a..5893a0dc 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -161,6 +161,16 @@ describe('Config', () => { expect(Config.primaryHost.fullUrl).toStrictEqual(expected); }); + + test('should manage double slash', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2/'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = '/mypath'; + + const expected = 'http://10.0.0.2/mypath'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + }); }); describe('secondaryHosts', () => { @@ -217,6 +227,7 @@ describe('Config', () => { process.env['SECONDARY_HOST_3_PASSWORD'] = 'mypassword3'; process.env['SECONDARY_HOST_3_PATH'] = '/mypath'; process.env['SECONDARY_HOST_5_BASE_URL'] = 'http://10.0.0.7'; + process.env['SECONDARY_HOST_5_PASSWORD'] = 'mypassword4'; process.env['SECONDARY_HOST_5_PATH'] = '/mypath'; expect(Config.secondaryHosts).toEqual([ diff --git a/src/config.ts b/src/config.ts index b73d4191..e7c1d67f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -173,19 +173,18 @@ export class Host { password: string; constructor(baseUrl: string, password: string, path?: string) { - this.baseUrl = baseUrl; + this.baseUrl = this.trimTrailingSlash(baseUrl) ?? baseUrl; this.password = password; - if (path && path.endsWith('/')) { - path = path.slice(0, path.length - 1); - } - if (path && !path.startsWith('/')) { path = '/' + path; } - this.path = path ?? '/admin'; - + this.path = this.trimTrailingSlash(path) ?? '/admin'; this.fullUrl = this.baseUrl + this.path; } + + private trimTrailingSlash(url: string | undefined): string | undefined { + return url?.endsWith('/') ? url.slice(0, url.length - 1) : url; + } } From 00ecf8c7b72ed9c165de1845fd25a620b7b6d48d Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Sat, 29 Oct 2022 08:58:59 +0000 Subject: [PATCH 5/8] Lift any path from the BaseURL and prepend to Path --- src/config.test.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++++ src/config.ts | 20 +++++++++++----- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index 5893a0dc..94b74a29 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -103,6 +103,14 @@ describe('Config', () => { }); describe('paths', () => { + test('should recognise https scheme', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'https://10.0.0.2'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + + const expected = 'https://10.0.0.2'; + + expect(Config.primaryHost.baseUrl).toStrictEqual(expected); + }); test('should add default /admin path when not provided', () => { process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2'; process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; @@ -118,6 +126,17 @@ describe('Config', () => { process.env['PRIMARY_HOST_PATH'] = '/mypath'; const expected = 'http://10.0.0.2/mypath'; + const expectedPath = '/mypath'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + expect(Config.primaryHost.path).toStrictEqual(expectedPath); + }); + + test('should handle single slash in base url', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2/'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + + const expected = 'http://10.0.0.2/admin'; expect(Config.primaryHost.fullUrl).toStrictEqual(expected); }); @@ -171,6 +190,44 @@ describe('Config', () => { expect(Config.primaryHost.fullUrl).toStrictEqual(expected); }); + + test('should handle path provided in base url', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2/mypath/admin'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = ''; + + const expected = 'http://10.0.0.2/mypath/admin'; + const expectedPath = '/mypath/admin'; + const expectedBase = 'http://10.0.0.2'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + expect(Config.primaryHost.path).toStrictEqual(expectedPath); + expect(Config.primaryHost.baseUrl).toStrictEqual(expectedBase); + }); + + test('should combine path from base url and path env', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2/mypath'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = '/admin'; + + const expected = 'http://10.0.0.2/mypath/admin'; + const expectedPath = '/mypath/admin'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + expect(Config.primaryHost.path).toStrictEqual(expectedPath); + }); + + test('should treat port as base url', () => { + process.env['PRIMARY_HOST_BASE_URL'] = 'http://10.0.0.2:8080/'; + process.env['PRIMARY_HOST_PASSWORD'] = 'mypassword'; + process.env['PRIMARY_HOST_PATH'] = '/mypath'; + + const expected = 'http://10.0.0.2:8080/mypath'; + const expectedPath = '/mypath'; + + expect(Config.primaryHost.fullUrl).toStrictEqual(expected); + expect(Config.primaryHost.path).toStrictEqual(expectedPath); + }); }); describe('secondaryHosts', () => { diff --git a/src/config.ts b/src/config.ts index e7c1d67f..f1714501 100644 --- a/src/config.ts +++ b/src/config.ts @@ -172,19 +172,27 @@ export class Host { fullUrl: string; password: string; - constructor(baseUrl: string, password: string, path?: string) { - this.baseUrl = this.trimTrailingSlash(baseUrl) ?? baseUrl; - this.password = password; + private static pathExtractor = RegExp('^(http[s]?:+//[^/s]+)([/]?[^?#]+)?'); + constructor(baseUrl: string, password: string, path = '/admin') { if (path && !path.startsWith('/')) { path = '/' + path; } - this.path = this.trimTrailingSlash(path) ?? '/admin'; + const includedPath = Host.pathExtractor.exec(baseUrl); + + if (includedPath && includedPath[1] && includedPath[2]) { + baseUrl = includedPath[1]; + path = (this.trimTrailingSlash(includedPath[2]) ?? '') + path; + } + + this.baseUrl = baseUrl; + this.password = password; + this.path = this.trimTrailingSlash(path); this.fullUrl = this.baseUrl + this.path; } - private trimTrailingSlash(url: string | undefined): string | undefined { - return url?.endsWith('/') ? url.slice(0, url.length - 1) : url; + private trimTrailingSlash(url: string): string { + return url.endsWith('/') ? url.slice(0, url.length - 1) : url; } } From 826fc6c9df931a29707a9bf41004af5a19927dc1 Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Sat, 29 Oct 2022 09:15:43 +0000 Subject: [PATCH 6/8] Doc update --- README.md | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 80ae4e5d..9a636855 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ services: SECONDARY_HOST_1_PASSWORD: 'your_password2' SECONDARY_HOST_2_BASE_URL: 'http://192.168.1.3' SECONDARY_HOST_2_PASSWORD: 'your_password3' + SECONDARY_HOST_3_BASE_URL: 'http://server:8080' + SECONDARY_HOST_3_PASSWORD: 'your_password4' + SECONDARY_HOST_3_PATH: '/apps/pi-hole' INTERVAL_MINUTES: 30 ``` @@ -80,27 +83,29 @@ It is recommended you run this service with Docker. ### Sync Configuration -| Environment Variable | Required | Default | Examples | Description | -| ----------------------------- | -------- | ------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `PRIMARY_HOST_BASE_URL` | Yes | N/A | `http://192.168.1.2` or `https://pihole.example.com` | The base URL of your Pi-hole, including the scheme (HTTP or HTTPS) and port but not including a following slash. | -| `PRIMARY_HOST_PASSWORD` | Yes | N/A | `mypassword` | The password used to log in to the admin interface. | -| `SECONDARY_HOST_(#)_BASE_URL` | Yes | N/A | `http://192.168.1.3` or `https://pihole2.example.com` | The base URL of your secondary Pi-hole, including the scheme (HTTP or HTTPS) and port but not including a following slash. Replace `(#)` with a number, starting at `1`, to add multiple secondary Pi-holes. | -| `SECONDARY_HOST_(#)_PASSWORD` | Yes | N/A | `mypassword2` | The password used to log in to the admin interface. | -| `INTERVAL_MINUTES` | No | 30 | Any non-zero positive integer, for example `5`, `30`, or `1440` | How long to wait between synchronizations. Defaults to five minutes. Remember that the DNS server on your secondary servers restarts everytime a sync is performed. | -| `UPDATE_GRAVITY` | No | `true` | `true`/`false` | Triggers a gravity update after a backup has been uploaded to a secondary Pi-hole. This updates adlists and restarts gravity. | -| `SYNC_WHITELIST` | No | `true` | `true`/`false` | Copies the whitelist | -| `SYNC_REGEX_WHITELIST` | No | `true` | `true`/`false` | Copies the regex whitelist | -| `SYNC_BLACKLIST` | No | `true` | `true`/`false` | Copies the blacklist | -| `SYNC_REGEXLIST` | No | `true` | `true`/`false` | Copies the regex blacklist | -| `SYNC_ADLIST` | No | `true` | `true`/`false` | Copies the adlist | -| `SYNC_CLIENT` | No | `true` | `true`/`false` | Copies clients | -| `SYNC_GROUP` | No | `true` | `true`/`false` | Copies groups | -| `SYNC_AUDITLOG` | No | `false` | `true`/`false` | Copies the audit log | -| `SYNC_STATICDHCPLEASES` | No | `false` | `true`/`false` | Copies static dhcp leases | -| `SYNC_LOCALDNSRECORDS` | No | `true` | `true`/`false` | Copies local DNS records | -| `SYNC_LOCALCNAMERECORDS` | No | `true` | `true`/`false` | Copies local CNAME records | -| `SYNC_FLUSHTABLES` | No | `true` | `true`/`false` | Clears existing data on the secondary (copy target) Pi-hole | -| `RUN_ONCE` | No | `false` | `true`/`false` | By default, `orbital-sync` runs indefinitely until stopped. Setting `RUN_ONCE` to `true` forces it to exit immediately after the first sync. | +| Environment Variable | Required | Default | Examples | Description | +| ----------------------------- | -------- | -------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `PRIMARY_HOST_BASE_URL` | Yes | N/A | `http://192.168.1.2` or `https://pihole.example.com` | The base URL of your Pi-hole, including the scheme (HTTP or HTTPS) and port but not including a following slash. | +| `PRIMARY_HOST_PATH` | No | `/admin` | `/` or `/apps/pi-hole` | The path to be appended to your base URL. The default Pi-hole path is `/admin`, which is added automatically. | +| `PRIMARY_HOST_PASSWORD` | Yes | N/A | `mypassword` | The password used to log in to the admin interface. | +| `SECONDARY_HOST_(#)_BASE_URL` | Yes | N/A | `http://192.168.1.3` or `https://pihole2.example.com` | The base URL of your secondary Pi-hole, including the scheme (HTTP or HTTPS) and port but not including a following slash. Replace `(#)` with a number, starting at `1`, to add multiple secondary Pi-holes. | +| `SECONDARY_HOST_(#)_PATH` | No | `/admin` | `/` or `/apps/pi-hole` | The path to be appended to your secondary base URL. The default Pi-hole path is `/admin`, which is added automatically. Replace `(#)` with a number, starting at `1`, to add multiple secondary Pi-holes. | +| `SECONDARY_HOST_(#)_PASSWORD` | Yes | N/A | `mypassword2` | The password used to log in to the admin interface. | +| `INTERVAL_MINUTES` | No | 30 | Any non-zero positive integer, for example `5`, `30`, or `1440` | How long to wait between synchronizations. Defaults to five minutes. Remember that the DNS server on your secondary servers restarts everytime a sync is performed. | +| `UPDATE_GRAVITY` | No | `true` | `true`/`false` | Triggers a gravity update after a backup has been uploaded to a secondary Pi-hole. This updates adlists and restarts gravity. | +| `SYNC_WHITELIST` | No | `true` | `true`/`false` | Copies the whitelist | +| `SYNC_REGEX_WHITELIST` | No | `true` | `true`/`false` | Copies the regex whitelist | +| `SYNC_BLACKLIST` | No | `true` | `true`/`false` | Copies the blacklist | +| `SYNC_REGEXLIST` | No | `true` | `true`/`false` | Copies the regex blacklist | +| `SYNC_ADLIST` | No | `true` | `true`/`false` | Copies the adlist | +| `SYNC_CLIENT` | No | `true` | `true`/`false` | Copies clients | +| `SYNC_GROUP` | No | `true` | `true`/`false` | Copies groups | +| `SYNC_AUDITLOG` | No | `false` | `true`/`false` | Copies the audit log | +| `SYNC_STATICDHCPLEASES` | No | `false` | `true`/`false` | Copies static dhcp leases | +| `SYNC_LOCALDNSRECORDS` | No | `true` | `true`/`false` | Copies local DNS records | +| `SYNC_LOCALCNAMERECORDS` | No | `true` | `true`/`false` | Copies local CNAME records | +| `SYNC_FLUSHTABLES` | No | `true` | `true`/`false` | Clears existing data on the secondary (copy target) Pi-hole | +| `RUN_ONCE` | No | `false` | `true`/`false` | By default, `orbital-sync` runs indefinitely until stopped. Setting `RUN_ONCE` to `true` forces it to exit immediately after the first sync. | Secondary hosts must be sequential (`SECONDARY_HOST_1_BASE_URL`, `SECONDARY_HOST_2_BASE_URL`, `SECONDARY_HOST_3_BASE_URL`, and so on) and start at number `1`. Any gaps (for example, `3` to `5` skipping `4`) will From 09fa52e9d533510b34645c0a2178b5a0d46bfb42 Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Tue, 1 Nov 2022 06:24:49 +0000 Subject: [PATCH 7/8] Change messages to use full URL --- src/client.test.ts | 16 ++++++++-------- src/client.ts | 28 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/client.test.ts b/src/client.test.ts index a200ff81..0f6b2562 100644 --- a/src/client.test.ts +++ b/src/client.test.ts @@ -35,7 +35,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ message: - 'There was an error logging in to "http://10.0.0.2" - are you able to log in with the configured password?', + 'There was an error logging in to "http://10.0.0.2/admin" - are you able to log in with the configured password?', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -56,7 +56,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ message: - 'No token could be found while logging in to "http://10.0.0.2" - are you able to log in with the configured password?', + 'No token could be found while logging in to "http://10.0.0.2/admin" - are you able to log in with the configured password?', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -78,7 +78,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ message: - 'A token was found but could not be validated while logging in to "http://10.0.0.2" - are you able to log in with the configured password?', + 'A token was found but could not be validated while logging in to "http://10.0.0.2/admin" - are you able to log in with the configured password?', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -124,7 +124,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ - message: 'Failed to download backup from "http://10.0.0.2".', + message: 'Failed to download backup from "http://10.0.0.2/admin".', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -143,7 +143,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ - message: 'Failed to download backup from "http://10.0.0.2".', + message: 'Failed to download backup from "http://10.0.0.2/admin".', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -208,7 +208,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ - message: 'Failed to upload backup to "http://10.0.0.2".', + message: 'Failed to upload backup to "http://10.0.0.2/admin".', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -225,7 +225,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ - message: 'Failed to upload backup to "http://10.0.0.2".', + message: 'Failed to upload backup to "http://10.0.0.2/admin".', verbose: { host: 'http://10.0.0.2', path: '/admin', @@ -262,7 +262,7 @@ describe('Client', () => { await expectError.toBeInstanceOf(ErrorNotification); await expectError.toMatchObject({ - message: 'Failed updating gravity on "http://10.0.0.2".', + message: 'Failed updating gravity on "http://10.0.0.2/admin".', verbose: { host: 'http://10.0.0.2', path: '/admin', diff --git a/src/client.ts b/src/client.ts index b8215cda..5833a90b 100644 --- a/src/client.ts +++ b/src/client.ts @@ -21,7 +21,7 @@ export class Client { ) {} public static async create(host: Host): Promise { - Log.info(chalk.yellow(`➡️ Signing in to ${host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Signing in to ${host.fullUrl}...`)); const fetch = fetchCookie(nodeFetch); await fetch(`${host.fullUrl}/index.php?login`, { method: 'GET' }); @@ -34,7 +34,7 @@ export class Client { }); if (response.status !== 200) throw new ErrorNotification({ - message: `There was an error logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, + message: `There was an error logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, verbose: { host: host.baseUrl, path: host.path, @@ -45,7 +45,7 @@ export class Client { const token = this.parseResponseForToken(host, await response.text()); - Log.info(chalk.green(`✔️ Successfully signed in to ${host.baseUrl}!`)); + Log.info(chalk.green(`✔️ Successfully signed in to ${host.fullUrl}!`)); return new this(fetch, host, token); } @@ -54,7 +54,7 @@ export class Client { const tokenDiv = root.querySelector('#token'); if (!tokenDiv) throw new ErrorNotification({ - message: `No token could be found while logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, + message: `No token could be found while logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, verbose: { host: host.baseUrl, path: host.path, @@ -65,7 +65,7 @@ export class Client { const token = tokenDiv.innerText; if (token.length != 44) throw new ErrorNotification({ - message: `A token was found but could not be validated while logging in to "${host.baseUrl}" - are you able to log in with the configured password?`, + message: `A token was found but could not be validated while logging in to "${host.fullUrl}" - are you able to log in with the configured password?`, verbose: { host: host.baseUrl, path: host.path, @@ -77,7 +77,7 @@ export class Client { } public async downloadBackup(): Promise { - Log.info(chalk.yellow(`➡️ Downloading backup from ${this.host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Downloading backup from ${this.host.fullUrl}...`)); const form = this.generateForm(); const response = await this.fetch( @@ -92,7 +92,7 @@ export class Client { response.headers.get('content-type') !== 'application/gzip' ) throw new ErrorNotification({ - message: `Failed to download backup from "${this.host.baseUrl}".`, + message: `Failed to download backup from "${this.host.fullUrl}".`, verbose: { host: this.host.baseUrl, path: this.host.path, @@ -103,12 +103,12 @@ export class Client { const data = await response.arrayBuffer(); - Log.info(chalk.green(`✔️ Backup from ${this.host.baseUrl} completed!`)); + Log.info(chalk.green(`✔️ Backup from ${this.host.fullUrl} completed!`)); return new Blob([data]); } public async uploadBackup(backup: Blob): Promise { - Log.info(chalk.yellow(`➡️ Uploading backup to ${this.host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Uploading backup to ${this.host.fullUrl}...`)); const form = this.generateForm(); form.append('action', 'in'); @@ -124,7 +124,7 @@ export class Client { const uploadText = await uploadResponse.text(); if (uploadResponse.status !== 200 || !uploadText.endsWith('OK')) throw new ErrorNotification({ - message: `Failed to upload backup to "${this.host.baseUrl}".`, + message: `Failed to upload backup to "${this.host.fullUrl}".`, verbose: { host: this.host.baseUrl, path: this.host.path, @@ -133,11 +133,11 @@ export class Client { } }); - Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.baseUrl}!`)); + Log.info(chalk.green(`✔️ Backup uploaded to ${this.host.fullUrl}!`)); Log.verbose(`Result:\n${chalk.blue(uploadText)}`); if (Config.updateGravity) { - Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.baseUrl}...`)); + Log.info(chalk.yellow(`➡️ Updating gravity on ${this.host.fullUrl}...`)); const gravityUpdateResponse = await this.fetch( `${this.host.fullUrl}/scripts/pi-hole/php/gravity.sh.php`, { method: 'GET' } @@ -151,7 +151,7 @@ export class Client { !updateText.endsWith('Pi-hole blocking is enabled') ) throw new ErrorNotification({ - message: `Failed updating gravity on "${this.host.baseUrl}".`, + message: `Failed updating gravity on "${this.host.fullUrl}".`, verbose: { host: this.host.baseUrl, path: this.host.path, @@ -160,7 +160,7 @@ export class Client { } }); - Log.info(chalk.green(`✔️ Gravity updated on ${this.host.baseUrl}!`)); + Log.info(chalk.green(`✔️ Gravity updated on ${this.host.fullUrl}!`)); Log.verbose(`Result:\n${chalk.blue(updateText)}`); } From 92c23391d47e37c54c40ea792aafc8e00aca1c9f Mon Sep 17 00:00:00 2001 From: Callum Bundy Date: Tue, 1 Nov 2022 06:25:27 +0000 Subject: [PATCH 8/8] remove todo --- src/config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index f1714501..2f1de918 100644 --- a/src/config.ts +++ b/src/config.ts @@ -46,7 +46,6 @@ export class Config { return this._secondaryHosts; } - //TODO look at usages static get allHostUrls(): string[] { return [this.primaryHost, ...this.secondaryHosts].map((host) => host.fullUrl); }