How to force kirby to load a static html page using the custom response type in the router?

Hello and thank you for looking at my question.

I have a static html export from a bookdown / R-markdown source and I would like to secure it so that only logged-in kirby users can view it.

I don’t want to touch the static html because I am still working on the “book” and so the export is still changing often.

Can I use routing to check the user status (logged in ?) on access to a subfolder and then pass the html inside it through Kirby unchanged?

I don’t need the static html to show up in any menu structure or to have the same styling as my kirby site. It is completely independent. I just need to check the user privileges on access and then show the static html.

Any ideas?

The plain html loads fine when the folder sits on the same level as the contents folder and the kirby folder. Can I get kirby to filter or route on a folder that sits outside the content folder?

I can get kirby to route to the folder outside the content from within, i.e. /data, but then it does not check the user privileges. I used the instructions here to do so. However, that does not help me.

I have now moved the folder back into /content, i.e. /content/data and I am trying to use the custom response type to force kirby to load the html and pass it through. I believe this should work, but I can’t seem to read the index2.html file in order to pass it on. I always see the File can not be found! message.

Here is my route in the config.php file:

  [  // protect the D.A.T.A.
    'pattern' => '/data(:all)',
    'action'  => function($all) {

      // if the user is not logged in, reroute to the login form.
      if (!kirby()->user()) {
        go('login?location=' . urlencode(kirby()->request()->path()));
      }

      // if logged in:
      if (F::exists('/data'.$all)) {
      //$html = file_get_contents('data/index2.html');
        $html = F::load('data/'.$all);
        return new Response($html, 'text/html'); 
      } else {
        return new Response('<html><body>File can not be found!</body></html>', 'text/html');
      }
    }
  ],

Shouldnt there be a slash both in the pattern and the file exists check?

I am not sure I understand. I am working from the local file system, not trying to load an external html page. I can’t seem to get it to read in the html file.

I just tried to add some html files in the panel but that is also not allowed. Is there some security feature that fails me in the background? Something that doesn’t let kirby load an index.html file and treat it like any other file?

If I understand correctly, you have a folder data, and inside that folder is an html file. Then I think your route should look like this (not tested):

[  // protect the D.A.T.A.
    'pattern' => '/data/(:all)',
    'action'  => function($all) {

      // if the user is not logged in, reroute to the login form.
      if (!kirby()->user()) {
        go('login?location=' . urlencode(kirby()->request()->path()));
      }

      // if logged in:
      if (F::exists('/data/' . $all)) {
      //$html = file_get_contents('data/index2.html');
        $html = F::load('data/' . $all);
        return new Response($html, 'text/html'); 
      } else {
        return new Response('<html><body>File can not be found!</body></html>', 'text/html');
      }
    }
  ],

Yes, the Panel doesn’t allow upload of HTML files. I’m not 100% sure if you can explicitly allow that or not.

Thank you so much for the quick reply. Unfortunately that still gives me the File can not be not found message. The if statement seems to fail. When I remove it, all I get in an empty reply – no html or other code in the response. Here is what I tried:

[  // protect the D.A.T.A.
    'pattern' => '/data/(:all)',
    'action'  => function($all) {

  // if the user is not logged in, reroute to the login form.
  if (!kirby()->user()) {
    go('login?location=' . urlencode(kirby()->request()->path()));
  }

  // if logged in:
    $html = F::load('data/' . $all);
    // $html = F::load('data/index.html'); // hard coding does not work either
    return new Response($html, 'text/html'); 
}
],

When I try the php native file opening method, then it throws an exception with the message: file_get_contents(data/index.html): Failed to open stream: No such file or directory. However, the file is there in /data/index.html.

I am trying to use the kirby authentication to check the user is logged in and then to go around it and to pass on the plain html. It appears to me, like the custom router response will allow this. I can write some html string into the Response and it will pass it. All I am stuck on now, is to load the html file instead of coding the html string by hand. Am I overlooking some major conceptual problem with this solution?

Something like this seems to have worked in kirby2.

Ah, sorry, of course you have to use the complete path to the file:

$html = F::load(kirby()->root('content') . '/data/' . $all);

Same for F::load()

Thank you so much! You are a star :star: !

The html is loading now. The links are working and the content is shown. The user authentication also works.

The only problem is, that it has absolutely no styling. The asset paths all seem to work, but it is shown without any styles applied.

Do you know if that is a side-effect of using the “Response” type to pass through the HTML? Is it not rendered as would normally be the case?

Don’t mind the missing bs4_style.css, that is never there and not required.

Which styles should be loaded, and where are they located?

According to the browser debug information, the styles and javascript are all successfully loading from a directory called ‘lib’ (and subfolders) inside the /data folder i.e. /data/lib/<subfolders>. However, the style doesn’t seem to get applied.

I am currently matching on any path i.e. ‘/data/(:all)’ and then returning a custom response of the type ‘text/html’. I am only guessing here, but could it also affect the assets that are being loaded from within the index.html file and thus somehow rendering them inert?

Yes! It was the mime type.
The assets were all passed as type ‘text/html’ and thus not rendered. Here now is the solution:

  [  // protect the D.A.T.A.
    'pattern' => '/data/(:all)',
    'action'  => function($all) {

      // if the user is not logged in, reroute to the login form.
      if (!kirby()->user()) {
        go('login?location=' . urlencode(kirby()->request()->path()));
      }

      // if logged in:
      $path = kirby()->root('content') . '/data/' . $all;
      if (F::exists($path)) {
        $f = F::read($path);
        $mime = F::mime($path);  //get the mime type dynamically
        return new Response($f, $mime); 
      }
    }
  ],
  [  // redirect to the index.html
    'pattern' => ['/data', '/data/'],
    'action'  => function() {
      return go('/data/index.html');
    }
  ],

Considering the few lines of code in the router that were required, this turned out to be quite the elegant solution to protect a static website that resides in a sub-directory inside the kirby content folder. I am very happy.

@texnixe Thank you so very much for all your help on this. I was quite stuck on that file loading issue.