From cbe52f7558eca6377fc92b9c23dd9e09d6b6bf46 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Tue, 30 Jan 2024 21:25:35 +0000 Subject: [PATCH 1/2] #1228: fix ordering of static content routes --- src/Private/Routes.ps1 | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Private/Routes.ps1 b/src/Private/Routes.ps1 index 427b115d2..0f8c7f09d 100644 --- a/src/Private/Routes.ps1 +++ b/src/Private/Routes.ps1 @@ -50,23 +50,31 @@ function Find-PodeRoute { } } - # is this a static route? - $isStatic = ($Method -ieq 'static') - # first ensure we have the method $_method = $PodeContext.Server.Routes[$Method] if ($null -eq $_method) { return $null } + # is this a static route? + $isStatic = ($Method -ieq 'static') + # if we have a perfect match for the route, return it if the protocol is right - $found = Get-PodeRouteByUrl -Routes $_method[$Path] -EndpointName $EndpointName - if (!$isStatic -and ($null -ne $found)) { - return $found + if (!$isStatic) { + $found = Get-PodeRouteByUrl -Routes $_method[$Path] -EndpointName $EndpointName + if ($null -ne $found) { + return $found + } } # otherwise, match the path to routes on regex (first match only) - $valid = @(foreach ($key in $_method.Keys) { + $paths = @($_method.Keys) + if ($isStatic) { + [array]::Sort($paths) + [array]::Reverse($paths) + } + + $valid = @(foreach ($key in $paths) { if ($Path -imatch "^$($key)$") { $key break From 9a57c272ad433fc9d86a760312dda0ac3bc64332 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Fri, 2 Feb 2024 22:19:45 +0000 Subject: [PATCH 2/2] #1228: adds a -RedirectToDefault switch to static routes --- src/Private/Context.ps1 | 5 +- src/Private/PodeServer.ps1 | 4 ++ src/Private/Routes.ps1 | 20 ++++++-- src/Private/Serverless.ps1 | 8 ++++ src/Public/Routes.ps1 | 93 +++++++++++++++++++++++++------------- 5 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/Private/Context.ps1 b/src/Private/Context.ps1 index 73e855bf6..dae736630 100644 --- a/src/Private/Context.ps1 +++ b/src/Private/Context.ps1 @@ -816,8 +816,9 @@ function Set-PodeWebConfiguration { # setup the main web config $Context.Server.Web = @{ Static = @{ - Defaults = $Configuration.Static.Defaults - Cache = @{ + Defaults = $Configuration.Static.Defaults + RedirectToDefault = [bool]$Configuration.Static.RedirectToDefault + Cache = @{ Enabled = [bool]$Configuration.Static.Cache.Enable MaxAge = [int](Protect-PodeValue -Value $Configuration.Static.Cache.MaxAge -Default 3600) Include = (Convert-PodePathPatternsToRegex -Paths @($Configuration.Static.Cache.Include) -NotSlashes) diff --git a/src/Private/PodeServer.ps1 b/src/Private/PodeServer.ps1 index ad10d44cd..557fda99d 100644 --- a/src/Private/PodeServer.ps1 +++ b/src/Private/PodeServer.ps1 @@ -189,6 +189,10 @@ function Start-PodeWebServer { if ($WebEvent.StaticContent.IsDownload) { Set-PodeResponseAttachment -Path $WebEvent.Path -EndpointName $WebEvent.Endpoint.Name } + elseif ($WebEvent.StaticContent.RedirectToDefault) { + $file = [System.IO.Path]::GetFileName($WebEvent.StaticContent.Source) + Move-PodeResponseUrl -Url "$($WebEvent.Path)/$($file)" + } else { $cachable = $WebEvent.StaticContent.IsCachable Write-PodeFileResponse -Path $WebEvent.StaticContent.Source -MaxAge $PodeContext.Server.Web.Static.Cache.MaxAge -Cache:$cachable diff --git a/src/Private/Routes.ps1 b/src/Private/Routes.ps1 index 0f8c7f09d..59131964a 100644 --- a/src/Private/Routes.ps1 +++ b/src/Private/Routes.ps1 @@ -139,6 +139,8 @@ function Find-PodeStaticRoute { $found = Find-PodeRoute -Method 'static' -Path $Path -EndpointName $EndpointName $download = ([bool]$found.Download) $source = $null + $isDefault = $false + $redirectToDefault = ([bool]$found.RedirectToDefault) # if we have a defined static route, use that if ($null -ne $found) { @@ -152,11 +154,13 @@ function Find-PodeStaticRoute { if (!$found.Download -and !(Test-PodePathIsFile $file) -and (Get-PodeCount @($found.Defaults)) -gt 0) { if ((Get-PodeCount @($found.Defaults)) -eq 1) { $file = [System.IO.Path]::Combine($file, @($found.Defaults)[0]) + $isDefault = $true } else { foreach ($def in $found.Defaults) { if (Test-PodePath ([System.IO.Path]::Combine($found.Source, $def)) -NoStatus) { $file = [System.IO.Path]::Combine($file, $def) + $isDefault = $true break } } @@ -171,6 +175,8 @@ function Find-PodeStaticRoute { $source = Find-PodePublicRoute -Path $Path $download = $false $found = $null + $isDefault = $false + $redirectToDefault = $false } # return nothing if no source @@ -179,11 +185,19 @@ function Find-PodeStaticRoute { } # return the route details + if ($redirectToDefault -and $isDefault) { + $redirectToDefault = $true + } + else { + $redirectToDefault = $false + } + return @{ Content = @{ - Source = $source - IsDownload = $download - IsCachable = (Test-PodeRouteValidForCaching -Path $Path) + Source = $source + IsDownload = $download + IsCachable = (Test-PodeRouteValidForCaching -Path $Path) + RedirectToDefault = $redirectToDefault } Route = $found } diff --git a/src/Private/Serverless.ps1 b/src/Private/Serverless.ps1 index a7aac81ca..d861501e1 100644 --- a/src/Private/Serverless.ps1 +++ b/src/Private/Serverless.ps1 @@ -85,6 +85,10 @@ function Start-PodeAzFuncServer { if ($WebEvent.StaticContent.IsDownload) { Set-PodeResponseAttachment -Path $WebEvent.Path -EndpointName $WebEvent.Endpoint.Name } + elseif ($WebEvent.StaticContent.RedirectToDefault) { + $file = [System.IO.Path]::GetFileName($WebEvent.StaticContent.Source) + Move-PodeResponseUrl -Url "$($WebEvent.Path)/$($file)" + } else { $cachable = $WebEvent.StaticContent.IsCachable Write-PodeFileResponse -Path $WebEvent.StaticContent.Source -MaxAge $PodeContext.Server.Web.Static.Cache.MaxAge -Cache:$cachable @@ -194,6 +198,10 @@ function Start-PodeAwsLambdaServer { if ($WebEvent.StaticContent.IsDownload) { Set-PodeResponseAttachment -Path $WebEvent.Path -EndpointName $WebEvent.Endpoint.Name } + elseif ($WebEvent.StaticContent.RedirectToDefault) { + $file = [System.IO.Path]::GetFileName($WebEvent.StaticContent.Source) + Move-PodeResponseUrl -Url "$($WebEvent.Path)/$($file)" + } else { $cachable = $WebEvent.StaticContent.IsCachable Write-PodeFileResponse -Path $WebEvent.StaticContent.Source -MaxAge $PodeContext.Server.Web.Static.Cache.MaxAge -Cache:$cachable diff --git a/src/Public/Routes.ps1 b/src/Public/Routes.ps1 index ae6d1d1e5..f1ab7bf84 100644 --- a/src/Public/Routes.ps1 +++ b/src/Public/Routes.ps1 @@ -476,6 +476,9 @@ One or more optional Scopes that will be authorised to access this Route, when u .PARAMETER User One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. +.PARAMETER RedirectToDefault +If supplied, the user will be redirected to the default page if found instead of the page being rendered as the folder path. + .EXAMPLE Add-PodeStaticRoute -Path '/assets' -Source './assets' @@ -484,6 +487,9 @@ Add-PodeStaticRoute -Path '/assets' -Source './assets' -Defaults @('index.html') .EXAMPLE Add-PodeStaticRoute -Path '/installers' -Source './exes' -DownloadOnly + +.EXAMPLE +Add-PodeStaticRoute -Path '/assets' -Source './assets' -Defaults @('index.html') -RedirectToDefault #> function Add-PodeStaticRoute { [CmdletBinding()] @@ -558,7 +564,10 @@ function Add-PodeStaticRoute { $DownloadOnly, [switch] - $PassThru + $PassThru, + + [switch] + $RedirectToDefault ) # check if we have any route group info defined @@ -611,6 +620,10 @@ function Add-PodeStaticRoute { $DownloadOnly = $RouteGroup.DownloadOnly } + if ($RouteGroup.RedirectToDefault) { + $RedirectToDefault = $RouteGroup.RedirectToDefault + } + if ($RouteGroup.IfExists -ine 'default') { $IfExists = $RouteGroup.IfExists } @@ -700,6 +713,10 @@ function Add-PodeStaticRoute { $Defaults = Get-PodeStaticRouteDefaults } + if (!$RedirectToDefault) { + $RedirectToDefault = $PodeContext.Server.Web.Static.RedirectToDefault + } + # convert any middleware into valid hashtables $Middleware = @(ConvertTo-PodeMiddleware -Middleware $Middleware -PSSession $PSCmdlet.SessionState) @@ -744,30 +761,31 @@ function Add-PodeStaticRoute { Write-Verbose "Adding Route: [$($Method)] $($Path)" $newRoutes = @(foreach ($_endpoint in $endpoints) { @{ - Source = $Source - Path = $Path - Method = $Method - Defaults = $Defaults - Middleware = $Middleware - Authentication = $Authentication - Access = $Access - AccessMeta = @{ + Source = $Source + Path = $Path + Method = $Method + Defaults = $Defaults + RedirectToDefault = $RedirectToDefault + Middleware = $Middleware + Authentication = $Authentication + Access = $Access + AccessMeta = @{ Role = $Role Group = $Group Scope = $Scope User = $User Custom = $CustomAccess } - Endpoint = @{ + Endpoint = @{ Protocol = $_endpoint.Protocol Address = $_endpoint.Address.Trim() Name = $_endpoint.Name } - ContentType = $ContentType - TransferEncoding = $TransferEncoding - ErrorType = $ErrorContentType - Download = $DownloadOnly - OpenApi = @{ + ContentType = $ContentType + TransferEncoding = $TransferEncoding + ErrorType = $ErrorContentType + Download = $DownloadOnly + OpenApi = @{ Path = $OpenApiPath Responses = @{ '200' = @{ description = 'OK' } @@ -777,8 +795,8 @@ function Add-PodeStaticRoute { RequestBody = @{} Authentication = @() } - IsStatic = $true - Metrics = @{ + IsStatic = $true + Metrics = @{ Requests = @{ Total = 0 StatusCodes = @{} @@ -1235,6 +1253,9 @@ One or more optional Scopes that will be authorised to access this Route, when u .PARAMETER User One or more optional Users that will be authorised to access this Route, when using Authentication with an Access method. +.PARAMETER RedirectToDefault +If supplied, the user will be redirected to the default page if found instead of the page being rendered as the folder path. + .EXAMPLE Add-PodeStaticRouteGroup -Path '/static' -Routes { Add-PodeStaticRoute -Path '/images' -Etc } #> @@ -1312,7 +1333,10 @@ function Add-PodeStaticRouteGroup { $AllowAnon, [switch] - $DownloadOnly + $DownloadOnly, + + [switch] + $RedirectToDefault ) if (Test-PodeIsEmpty $Routes) { @@ -1376,6 +1400,10 @@ function Add-PodeStaticRouteGroup { $DownloadOnly = $RouteGroup.DownloadOnly } + if ($RouteGroup.RedirectToDefault) { + $RedirectToDefault = $RouteGroup.RedirectToDefault + } + if ($RouteGroup.IfExists -ine 'default') { $IfExists = $RouteGroup.IfExists } @@ -1402,20 +1430,21 @@ function Add-PodeStaticRouteGroup { } $RouteGroup = @{ - Path = $Path - Source = $Source - Middleware = $Middleware - EndpointName = $EndpointName - ContentType = $ContentType - TransferEncoding = $TransferEncoding - Defaults = $Defaults - ErrorContentType = $ErrorContentType - Authentication = $Authentication - Access = $Access - AllowAnon = $AllowAnon - DownloadOnly = $DownloadOnly - IfExists = $IfExists - AccessMeta = @{ + Path = $Path + Source = $Source + Middleware = $Middleware + EndpointName = $EndpointName + ContentType = $ContentType + TransferEncoding = $TransferEncoding + Defaults = $Defaults + RedirectToDefault = $RedirectToDefault + ErrorContentType = $ErrorContentType + Authentication = $Authentication + Access = $Access + AllowAnon = $AllowAnon + DownloadOnly = $DownloadOnly + IfExists = $IfExists + AccessMeta = @{ Role = $Role Group = $Group Scope = $Scope