Pode icon indicating copy to clipboard operation
Pode copied to clipboard

Static Route doesn't resolve correctly link that start with ./

Open mdaneri opened this issue 1 year ago • 19 comments

Describe the Bug

I have this webpage

<!DOCTYPE html>
<!-- HTML for static distribution bundle build -->
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Swagger Editor</title>
  <style>
  * {
    box-sizing: border-box;
  }
  body {
    font-family: Roboto,sans-serif;
    font-size: 9px;
    line-height: 1.42857143;
    color: #444;
    margin: 0px;
  }

  #swagger-editor {
    font-size: 1.3em;
  }

  .container {
    height: 100%;
    max-width: 880px;
    margin-left: auto;
    margin-right: auto;
  }

  #editor-wrapper {
    height: 100%;
    border:1em solid #000;
    border:none;
  }

  .Pane2 {
    overflow-y: scroll;
  }

  </style>
  <link href="./swagger-editor-dist/swagger-editor.css" rel="stylesheet">
  <link rel="icon" type="image/png" href="./swagger-editor-dist/favicon-32x32.png" sizes="32x32" />
  <link rel="icon" type="image/png" href="./swagger-editor-dist/favicon-16x16.png" sizes="16x16" />
</head>

<body>
  <div id="swagger-editor"></div>
  <script src="./swagger-editor-dist/swagger-editor-bundle.js"> </script>
  <script src="./swagger-editor-dist/swagger-editor-standalone-preset.js"> </script>
  <script>
  window.onload = function() {
    // Build a system
    // info here https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md#core
    const editor = SwaggerEditorBundle({
      dom_id: '#swagger-editor',
      layout: 'StandaloneLayout',
      presets: [
        SwaggerEditorStandalonePreset
      ],
      queryConfigEnabled: false,
      url: "$($data.OpenApi)",
      requestSnippetsEnabled: true
    })

  window.editor = editor
    window.editor = editor
  }
  </script>
  

</body>

</html>

this page is published using

   # set a default path
        $Path = Protect-PodeValue -Value $Path -Default "/docs/swagger-editor"
        if ([string]::IsNullOrWhiteSpace($Title)) {
            throw "No route path supplied for $($Type) page"
        }
        if (Test-OpenAPIVersion -Version 3.1 -DefinitionTag $DefinitionTag) {
            throw "This version on Swagger-Editor doesn't support OpenAPI 3.1"
        }
        # setup meta info
        $meta = @{
            Title             = $Title
            OpenApi           = $OpenApiUrl
            DarkMode          = $DarkMode
            DefinitionTag     = $DefinitionTag
            SwaggerEditorDist = "$Path/swagger-editor-dist"
        }
        $route = Add-PodeRoute -Method Get -Path $Path `
            -Middleware $Middleware -ArgumentList $meta -EndpointName $EndpointName -PassThru -ScriptBlock {
            param($meta)
            $Data = @{
                Title             = $meta.Title
                OpenApi           = $meta.OpenApi
                SwaggerEditorDist = $meta.SwaggerEditorDist
            }

            $podeRoot = Get-PodeModuleMiscPath
            Write-PodeFileResponse -Path ([System.IO.Path]::Combine($podeRoot, 'default-swagger-editor.html.pode')) -Data $Data
        }

        $swaggerEditorPath = Join-Path -Path $(Get-PodeModuleMiscPath) -ChildPath 'swagger-editor-dist'
        Add-PodeStaticRoute -Path  $meta.SwaggerEditorDist -Source $swaggerEditorPath -EndpointName $EndpointName

        $PodeContext.Server.OpenAPI[$DefinitionTag].hiddenComponents.viewer['editor'] = $Path

all static files are under the swagger-editor-dist folder

Problem

when I open the page I get errors that all links are not valid I checked the content of $WebEvent and this is the result

{
    "AcceptEncoding": "",
    "Path": "/docs/swagger-editor-dist/favicon-16x16.png",
    "Response": {
        "StatusCode": 200,
        "SendChunked": false,
        "Headers": {
            "Count": 5,
            "Keys": "Access-Control-Allow-Origin Access-Control-Allow-Credentials Access-Control-Max-Age Access-Control-Allow-Headers Access-Control-Allow-Methods"
        },
        "OutputStream": {
            "CanRead": true,
            "CanSeek": true,
            "CanWrite": true,
            "Capacity": 0,
            "Length": 0,
            "Position": 0,
            "CanTimeout": false,
            "ReadTimeout": null,
            "WriteTimeout": null
        },
        "Sent": false,
        "IsDisposed": false,
        "StatusDescription": "OK",
        "ContentLength64": 0,
        "ContentType": "",
        "HttpResponseLine": "HTTP/1.1 200 OK\r\n"
    },
    "Streamed": true,
    "Data": null,
    "Cookies": {},
    "Request": {
        "HttpMethod": "GET",
        "QueryString": null,
        "Protocol": "HTTP/1.1",
        "ProtocolVersion": "1.1",
        "ContentType": "",
        "ContentLength": 0,
        "ContentEncoding": {
            "Preamble": null,
            "BodyName": "utf-8",
            "EncodingName": "Unicode (UTF-8)",
            "HeaderName": "utf-8",
            "WebName": "utf-8",
            "WindowsCodePage": 1200,
            "IsBrowserDisplay": true,
            "IsBrowserSave": true,
            "IsMailNewsDisplay": true,
            "IsMailNewsSave": true,
            "IsSingleByte": false,
            "EncoderFallback": "System.Text.EncoderReplacementFallback",
            "DecoderFallback": "System.Text.DecoderReplacementFallback",
            "IsReadOnly": true,
            "CodePage": 65001
        },
        "TransferEncoding": "",
        "UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "UrlReferrer": "http://localhost:8081/docs/swagger-editor",
        "Url": "http://localhost:8081/docs/swagger-editor-dist/favicon-16x16.png",
        "Headers": {
            "Sec-Fetch-Mode": "no-cors",
            "Sec-Fetch-Dest": "image",
            "Referer": "http://localhost:8081/docs/swagger-editor",
            "Sec-GPC": "1",
            "sec-ch-ua-platform": "\"Windows\"",
            "Cookie": "pode.sid=s:ef35f34c-673a-4b49-9ca5-0c8912c566d0.LnTLYqmm6jNkNDs2bi5OYUAjyMCgZdyBPMU72Rmafhg=",
            "Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
            "Connection": "keep-alive",
            "sec-ch-ua-mobile": "?0",
            "Host": "localhost:8081",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en",
            "Accept-Encoding": "gzip, deflate, br",
            "sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Brave\";v=\"120\"",
            "Sec-Fetch-Site": "same-origin"
        },
        "RawBody": [],
        "Host": "localhost:8081",
        "AwaitingBody": false,
        "Form": null,
        "Body": "",
        "CloseImmediately": false,
        "IsProcessable": true,
        "RemoteEndPoint": {
            "AddressFamily": 2,
            "Address": "127.0.0.1",
            "Port": 63863
        },
        "LocalEndPoint": {
            "AddressFamily": 2,
            "Address": "127.0.0.1",
            "Port": 8081
        },
        "IsSsl": false,
        "SslUpgraded": false,
        "IsKeepAlive": true,
        "InputStream": {
            "Socket": "System.Net.Sockets.Socket",
            "CanRead": true,
            "CanSeek": false,
            "CanWrite": true,
            "CanTimeout": true,
            "ReadTimeout": 100,
            "WriteTimeout": -1,
            "DataAvailable": false,
            "Length": null,
            "Position": null
        },
        "Certificate": null,
        "AllowClientCertificate": false,
        "TlsMode": 0,
        "ClientCertificate": null,
        "ClientCertificateErrors": 0,
        "Protocols": 3120,
        "Error": null,
        "IsAborted": false,
        "IsDisposed": false,
        "Address": "127.0.0.1:8081",
        "Scheme": "Http",
        "Type": 1,
        "IsHttp": true,
        "IsWebSocket": false,
        "IsSmtp": false,
        "IsTcp": false,
        "IsUnknown": false
    },
    "Timestamp": "2024-01-05T03:07:52.1728449Z",
    "ContentType": "",
    "Files": null,
    "Parameters": null,
    "Auth": {},
    "StaticContent": null,
    "Lockable": {},
    "PendingCookies": {},
    "TransferEncoding": "",
    "Ranges": null,
    "OnEnd": [],
    "Endpoint": {
        "Address": "localhost:8081",
        "Protocol": "http",
        "Name": null
    },
    "Method": "get",
    "Route": null,
    "Query": null,
    "ErrorType": null
}

"Path" is /docs/swagger-editor-dist/favicon-16x16.png instead of /docs/swagger-editor/swagger-editor-dist/favicon-16x16.png

mdaneri avatar Jan 05 '24 03:01 mdaneri

Hi @mdaneri,

The translation is done at the HTML level client side. If I remember rightly relative paths in HTML are based on the current "folder" which in this case would be /docs as /swagger-editor is the document you're viewing rather than a folder (as far as the client side is concerned). So you'd need to add ./swagger-editor to all the URLs - which which work if someone was using a custom URL for the editor.

You do have the static content path available in $data.SwaggerEditorDist, so dynamic generating the href URLs could work:

<link rel="icon" type="image/png" href="$($data.SwaggerEditorDist)/favicon-32x32.png" sizes="32x32" />

Badgerati avatar Jan 17 '24 23:01 Badgerati

Yes but this is a workaround I have to use to make it works.

If I use any other webserver (IIS, Apache or anything else) and my page is http://localhost/rootpage/test/index.html ./swagger-editor-dist/swagger-editor.cssinsideindex.htmlredirects tohttp://localhost/rootpage/test/swagger-editor/swagger-editor-dist/swagger-editor.cssnot tohttp://localhost/rootpage/swagger-editor/swagger-editor-dist/swagger-editor.css`

mdaneri avatar Jan 17 '24 23:01 mdaneri

But the correct option is what's occurring no?

Your page is at http://localhost/docs/swagger-editor and your href is ./swagger-editor-dist/swagger-editor.css, therefore it would load the content from http://localhost/docs/swagger-editor-dist/swagger-editor.css

If the page was at http://localhost/docs/swagger-editor/index.html as it was loading the css from http://localhost/docs/swagger-editor-dist/swagger-editor.css I'd agree, but in the former example /swagger-editor is the document rather than a folder 🤔

Badgerati avatar Jan 17 '24 23:01 Badgerati

I created a sample to replicate the issue. https://github.com/mdaneri/Pode/tree/develop/examples/SwaggerEditor. this is the way I'm publishing the editor

 # STATIC asset folder route
    Add-PodeStaticRoute -Path '/editor' -Source './www' -Defaults @('index.html')
    Add-PodeStaticRoute -Path '/editor/swagger-editor-dist' -Source './swagger-editor-dist'
    Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
        Move-PodeResponseUrl -Url '/editor'
    }

It should work, but it's not.

mdaneri avatar Jan 17 '24 23:01 mdaneri

Looks like the issue is not related to the translation but the static routes order.

I changed the code and now more or less is working

   # STATIC asset folder route
    Add-PodeStaticRoute -Path '/editor/swagger-editor-dist' -Source './swagger-editor-dist'
    Add-PodeStaticRoute -Path '/editor' -Source './www' -Defaults @('index.html')

    Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
        Move-PodeResponseUrl -Url '/editor'
    }

What is not working is the -Defaults @('index.html') I have to specify http://localhost:8085/editor/index.html to access the page

This looks like a bug. Can you please confirm it?

mdaneri avatar Jan 18 '24 00:01 mdaneri

Ah yes, the ordering that hierarchical routes like that are created in does matter.

It's been reported before that the -Defaults doesn't work, but I could never reproduce it; I'll try again just in case.

Badgerati avatar Jan 20 '24 18:01 Badgerati

Hi @mdaneri,

I just tested myself and http://localhost:8085/editor does load the index.html page, however it is just a white screen - which makes sense as the ./swagger-editor-dist/swagger-editor.css references would call http://localhost:8085/swagger-editor-dist/swagger-editor.css which doesn't exist.

If I do call http://localhost:8085/editor/index.html directly the page loads normally, because the relative ./swagger-editor-dist/swagger-editor.css path would map from the /editor folder in the URL.

Other than that, the -Defaults is working and using index.html for me; do you get something different like a 404 page?

Badgerati avatar Jan 20 '24 19:01 Badgerati

I created 2 samples and removed the swagger-editor-dist folder. Now is using the /src/Misc/swagger-editor-dist folder as source.

  • swagger-editor.ps1 - the editor works but not -Defaults @('index.html')
  • swagger-editor-broken.ps1 - the editor doesn't work but -Defaults @('index.html') works as expected

mdaneri avatar Jan 22 '24 03:01 mdaneri

Hi @mdaneri,

I'm still seeing the same as my previous response. For the swagger-editor-broken.ps1 I would expect this to not work, as the order the static routes are created in does matter, and the longer /editor/swagger-editor-dist route does need to be created before the /editor route.

For swagger-editor.ps1 though, this does load the index.html content when navigating to http://localhost:8085/editor for me; though I do get a white screen because the CSS/JS it's trying to load can't be found: image

However if I update the index.html CSS/JS to include /editor as well:

<link href="./editor/swagger-editor-dist/swagger-editor.css" rel="stylesheet">

Then loading the page works: image

This is expected to me, because the CSS/JS relative paths will be looking for http://localhost:8085/swagger-editor-dist/swagger-editor.css when loaded from http://localhost:8085/editor, because the /editor part is a page not a folder like it would be with http://localhost:8085/editor/index.html.

Is this the same as what you're seeing, or are you seeing something different - like a 404 page instead of a white page?

Badgerati avatar Jan 27 '24 20:01 Badgerati

Sorry, I misread your previous comment when you said that "the ordering that hierarchical routes like that are created does matter." I understood the opposite 😀

Do we have this limitation in the documentation?

Do you think is worth spending time to fix it?

mdaneri avatar Jan 27 '24 21:01 mdaneri

It'd be a difficult one to fix without breaking people's integrations who are already aware that the order matters. Best to fix it in 3.0.0 😉

For now, adding a section into the /Tutorials/Routes/Utilities/StaticContent page in the docs it good enough.

Badgerati avatar Jan 28 '24 15:01 Badgerati

Maybe a check with a warning can be good enough

mdaneri avatar Jan 28 '24 21:01 mdaneri

I had a thought about this last night; it should be fine to fix for just static content. I've an idea on how to quickly fix it, which I'll commit in a moment.

Badgerati avatar Jan 30 '24 21:01 Badgerati

Please can you fix the default page issue?

mdaneri avatar Jan 30 '24 21:01 mdaneri

What is the problem you're actually seeing with -Default, have you any screenshots, etc.? Because for me it's working as expected.

Badgerati avatar Jan 30 '24 21:01 Badgerati

That commit, for static content only, will sort the paths it's searching on into the correct order. I've tested it using your editor examples, and flipping the two static route definitions around, and all works now for me regardless of order the routes are created in.

Badgerati avatar Jan 30 '24 21:01 Badgerati

Yes, it is working, and the route order is not relevant now. But still, the page doesn't work unless I use http://localhost:8085/editor/index.html

The page should work without the need to specify index.html in the URL. if you try http://localhost:8085 automatically, you are redirected to http://localhost:8085/editor, and the index.html is loaded, but the links on the page are broken.

image image

This is the issue with -Default

mdaneri avatar Jan 31 '24 19:01 mdaneri

The way -Default is working is as expected, the issue is caused by the way relative paths work in HTML.

The only way I can fix this is to add a switch parameter which tells Pode to redirect to /editor/index.html instead of rendering the index.html file as /editor.

Badgerati avatar Feb 02 '24 21:02 Badgerati

I've added a -RedirectToDefault switch to Add-PodeStaticRoute, which will instead reirect to /editor/index.html instead of loading the index.html file in place.

Badgerati avatar Feb 02 '24 22:02 Badgerati