diff --git a/PiHoleShell/PiHoleShell.psm1 b/PiHoleShell/PiHoleShell.psm1 index 92b77e2..001e1ec 100644 --- a/PiHoleShell/PiHoleShell.psm1 +++ b/PiHoleShell/PiHoleShell.psm1 @@ -16,7 +16,7 @@ foreach ($File in $PrivateFunctions) { Export-ModuleMember -Function @( #Actions - 'Update-PiHoleActionsGravity', 'Invoke-PiHoleFlushNetwork', 'Restart-PiHoleDnsService' ` + 'Update-PiHoleActionsGravity', 'Invoke-PiHoleFlushNetwork', 'Invoke-PiHoleFlushLogs', 'Restart-PiHoleDnsService' ` #Authentication 'Remove-PiHoleCurrentAuthSession' , 'Get-PiHoleCurrentAuthSession', 'Remove-PiHoleAuthSession', ` #GroupManagement diff --git a/PiHoleShell/Public/Actions/Invoke-PiHoleFlushLogs.ps1 b/PiHoleShell/Public/Actions/Invoke-PiHoleFlushLogs.ps1 new file mode 100644 index 0000000..ec03eb8 --- /dev/null +++ b/PiHoleShell/Public/Actions/Invoke-PiHoleFlushLogs.ps1 @@ -0,0 +1,71 @@ +function Invoke-PiHoleFlushLogs { + <# +.SYNOPSIS +Flushes the DNS logs + +.DESCRIPTION +Flushes the Pi-hole DNS logs. This empties the DNS log file and purges the most recent 24 +hours of query history from both the long-term database and FTL's internal memory. + +.PARAMETER PiHoleServer +The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100" + +.PARAMETER Password +The API Password you generated from your PiHole server + +.PARAMETER IgnoreSsl +Set to $true to skip SSL certificate validation + +.PARAMETER RawOutput +This will dump the response instead of the formatted object + +.EXAMPLE +Invoke-PiHoleFlushLogs -PiHoleServer "http://pihole.domain.com:8080" -Password "your-app-password" + #> + [CmdletBinding(HelpUri = 'https://ftl.pi-hole.net/master/docs/#post-/action/flush/logs')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Flushes PiHole logs')] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "Password")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Logs matches the Pi-hole API's own endpoint name, /action/flush/logs")] + param ( + [Parameter(Mandatory = $true)] + [System.URI]$PiHoleServer, + [Parameter(Mandatory = $true)] + [string]$Password, + [bool]$IgnoreSsl = $false, + [bool]$RawOutput = $false + ) + + try { + $Sid = Request-PiHoleAuth -PiHoleServer $PiHoleServer -Password $Password -IgnoreSsl $IgnoreSsl + + $Params = @{ + Headers = @{sid = $($Sid) } + Uri = "$($PiHoleServer.OriginalString)/api/action/flush/logs" + Method = "Post" + ContentType = "application/json" + SkipCertificateCheck = $IgnoreSsl + } + + $Response = Invoke-RestMethod @Params + + if ($RawOutput) { + Write-Output $Response + } + else { + $Object = [PSCustomObject]@{ + Status = "Flushed" + } + Write-Output $Object + } + } + + catch { + Write-Error -Message $_.Exception.Message + } + + finally { + if ($Sid) { + Remove-PiHoleCurrentAuthSession -PiHoleServer $PiHoleServer -Sid $Sid -IgnoreSsl $IgnoreSsl + } + } +} diff --git a/PiHoleShell/Public/Actions/Invoke-PiHoleFlushNetwork.ps1 b/PiHoleShell/Public/Actions/Invoke-PiHoleFlushNetwork.ps1 index 2c47c34..d361243 100644 --- a/PiHoleShell/Public/Actions/Invoke-PiHoleFlushNetwork.ps1 +++ b/PiHoleShell/Public/Actions/Invoke-PiHoleFlushNetwork.ps1 @@ -4,7 +4,7 @@ function Invoke-PiHoleFlushNetwork { Flushes the network table. This includes removing both all known devices and their associated addresses. .DESCRIPTION -Flushes the Pi-hole log file (/var/log/pihole/pihole.log). +Flushes Pi-hole's network table, removing all known devices and their associated addresses. .PARAMETER PiHoleServer The URL to the PiHole Server, for example "http://pihole.domain.com:8080", or "http://192.168.1.100" diff --git a/README.md b/README.md index 7180dec..2a7c11a 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ Functions marked 🚧 are still under active development — signatures and outp | Function | Description | |---|---| +| `Invoke-PiHoleFlushLogs` | Flushes the DNS logs | | `Invoke-PiHoleFlushNetwork` | Flushes the network table. This includes removing both all known devices and their associated addresses. | | `Restart-PiHoleDnsService` | Restarts the pihole-FTL service | | `Update-PiHoleActionsGravity` | Update Pi-hole's adlists by running pihole -g | diff --git a/tests/Actions/Invoke-PiHoleFlushLogs.Integration.Tests.ps1 b/tests/Actions/Invoke-PiHoleFlushLogs.Integration.Tests.ps1 new file mode 100644 index 0000000..0771c2d --- /dev/null +++ b/tests/Actions/Invoke-PiHoleFlushLogs.Integration.Tests.ps1 @@ -0,0 +1,46 @@ +# Requires -Module Pester +# +# Integration tests that call a REAL Pi-hole server. Configure tests/IntegrationConfig.local.ps1 +# (copy it from IntegrationConfig.example.ps1) before running. Tests are skipped automatically +# if that file is missing. + +# Config availability must be known at discovery time so the -Skip parameter on each It block +# (evaluated during discovery, before BeforeAll runs) sees the correct value. +$script:ConfigAvailable = Test-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'IntegrationConfig.local.ps1') + +Describe 'Invoke-PiHoleFlushLogs (Integration)' -Tag 'Integration' { + BeforeAll { + Import-Module .\PiHoleShell\PiHoleShell.psm1 -Force + + # Recomputed here (not read from the discovery-time $script:ConfigAvailable above) because + # Pester runs discovery and run in separate scopes, so BeforeAll cannot see that value. + $configPath = Join-Path (Split-Path $PSScriptRoot -Parent) 'IntegrationConfig.local.ps1' + if (Test-Path $configPath) { + . $configPath + $script:PiHoleServer = $PiHoleServer + $script:PiHoleToken = $PiHoleToken + $script:PiHoleIgnoreSsl = $PiHoleIgnoreSsl + } + } + + It 'flushes the DNS logs and returns a formatted status' -Skip:(-not $script:ConfigAvailable) { + $result = Invoke-PiHoleFlushLogs -PiHoleServer $script:PiHoleServer -Password $script:PiHoleToken -IgnoreSsl $script:PiHoleIgnoreSsl + $result | Format-List | Out-String | Write-Host + + $result | Should -Not -BeNullOrEmpty + $result.Status | Should -Be 'Flushed' + } + + It 'returns the raw API response when RawOutput is set' -Skip:(-not $script:ConfigAvailable) { + $result = Invoke-PiHoleFlushLogs -PiHoleServer $script:PiHoleServer -Password $script:PiHoleToken -IgnoreSsl $script:PiHoleIgnoreSsl -RawOutput $true + $result | Format-List | Out-String | Write-Host + + $result.status | Should -Be 'success' + } + + It 'errors when given a bad password' -Skip:(-not $script:ConfigAvailable) { + $result = Invoke-PiHoleFlushLogs -PiHoleServer $script:PiHoleServer -Password 'definitely-not-the-real-token' -IgnoreSsl $script:PiHoleIgnoreSsl -ErrorVariable errOut -ErrorAction SilentlyContinue + + $errOut | Should -Not -BeNullOrEmpty + } +}