Clojure server configuration for SPA's with client-side routing - clojurescript

I'm working on a ClojureScript Single-Page app with routing on client-side. I'd like to implement a simple server with would serve my index.html as well as CSS/JS. The idea is to pass all requests apart from /static/* down to client-side and allow my SPA to deal with it.
It turned out to be surprisingly difficult. The snippet below is something I came up, but it doesn't work.
(defroutes routes
(GET "/" []
(resp/content-type template "text/html"))
(context "/static" []
(route/resources "/css" {:root "css"})
(route/resources "/js" {:root "js"}))
(route/not-found
(resp/content-type template "text/html")))
I use boot-http with custom handler. Any chance it could cause it?

Sounds like you don't need a backend at all; you can develop with boot-http/boot-reload and deploy to gh-pages, firebase, aws or any static file host. See https://github.com/martinklepsch/tenzing for a template project #nobackend.

Related

How can I get window.location.href in VuePress V2

I'm trying to display a dynamic link to a Quiz from inside my VuePress V2 documentation. Since that game will be hosted on several IP's, i need to dynamically generate that URL.
Game and documentation will always run on the same IP, but different port.
I am looking for something like this in my markup file:
# Quiz
[Click here to start the quiz](<script>document.write(window.location.href.replace('3000','8080')</script>)
I know I can't use script, but i am hoping for one of the following:
Like i can keep the protocol by calling //example.com and keeping the URL by calling /subfolder, maybe there is a way to call :8080// or sth...
I can make use of the fact that Vue Syntax is allowed. There is a way I can implement logic into .md files by writing
I have { 1+1 } hands.
That will convert to
I have 2 hands.
I can write a custom Vue component and in there reference window.location.href.

Serve dynamic content with Firebase Hosting/Functions in EU

I would like to serve a Next.js app in europe using Firebase Hosting & Functions capabilities.
I do understand from the doc that:
If you are using HTTP functions to serve dynamic content for Firebase
Hosting, you must use us-central1
and that
Firebase Hosting supports Cloud Functions in us-central1 only
It's pretty clear: you must use us-central. But my main target is Europe..
I've read the following on the Cloud Functions locations guide:
For HTTP and callable functions, we recommend that you first set your
function to the destination region, or closest to where most expected
customers are located, and then alter your original function to
redirect its HTTP request to the new function (they can have the same
name). [Solution 1] If clients of your HTTP function support
redirects, you can simply change your original function to return an
HTTP redirect status (301) along with the URL of your new function.
[Solution 2] If your clients do not handle redirects well, you can
proxy the request from the original function to the new function by
initiating a new request from the original function to the new
function. The final step is to ensure that all clients are calling the
new function.
I've highlighted what seems to be two solutions to my initial problem:
Solution 1
Have a us-central1 function that send a 301 redirection to https://europe-west1-[myProject].cloudfunctions.net/[myEuropeanFunction]
Have a europe-west1 function that does the job (in my case, serve the Next.js app)
Happily using Firestore located in europe-west1
This would only work if clients of the HTTP function support redirects. In my case, it's fine: all browsers support redirection.
exports.nextServer = functions
.https
.onRequest((req, res) => {
res.set('location', 'https://europe-west1-<my-project>.cloudfunctions.net/nextServerEurope');
res.status(301).send()
});
exports.nextServerEurope = functions
.region('europe-west1')
.https
.onRequest((req, res) => {
return server.prepare().then(() => nextjsHandle(req, res));
});
The issue with that solution is that the URL changes in the browser to https://europe-west1-.cloudfunctions.net/nextServerEurope :-/
Solution 2
Have a us-central1 function that initiate a new/proxy request to the europe-west1 function
Have the same europe-west1 function that does the job (in my case, serve the Next.js app)
Still happily using Firestore located in europe-west1
By proxy request (as suggested in the guide), it would mean using a lib like axios I suppose. I know there are some libraries to perform proxy request available for node as well.
However, with that solution, the first issue I can think of is the unnecessary delay introcuded by passing by the us endpoint:
client -> us endpoint -> eu endpoint -> do stuff -> us endpoint -> client
Billing wise, I'm wondering what would be the impact..
I know that two services from different regions calling each others can increase the latency and the billing (egress).
With the first solution, there's no egress traffic as it's only a redirection to the european endpoint. But the redirection itself is not a valid solution in my case.
It's unclear for me what would be the additional billing cost with the second solution (beside the latency cost): is the traffic for the proxy request from us to eu going to be expensive?
To wrap-up:
The solution 1 is easy but leads to a non-transparent redirection
The solution 2 seems ok but it requires extra http request which leads to extra-latency (and potentially extra billing)
In the end, both solutions doesn't seem quite okay.
Therefore my question:
How do you serve in Europe dynamic content using Firebase Hosting and Functions?
Firebase Hosting only supports Cloud Functions in Us-Central as you mentioned and as stated in the Firebase Hosting Official Documentation.
I have created a Feature Request in Public Issue Tracker to support other regions when using Firebase Hosting with Cloud Functions. Please note, there is no ETA when this will be implemented.
So as #Doug Stevenson suggest, you can use Firebase Hosting with Cloud Run instead to serve your Dynamic Content.
Just to update. As of August 2022.
Finally, latency issue can be solved easily for now.
Firebase Hosting rewrites to CF3 are able to be done to any CF3
region, not just us-central1.
Reference: Feature Request Ticket

How do you cache a route with a wildcard in a PWA service worker?

I'm trying to convert my application to a PWA and I understand that I need to provide a complete list of endpoints and static files to the service worker, so it can manage the caching. In all the examples I'm finding, the pages are static links like /report. But in most real world applications, the pages contain dynamic parts, like /report/{reportId:int}. How do you tell the service worker about such an endpoint?
You can use javascript test() method and check if the url matches the regExp expression that would fit your url.
You can read about it on this official guide:
https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook#putting_it_together
extract:
if (/^\/article\//.test(requestURL.pathname)) {
event.respondWith(/* some other combination of patterns */);
return;
}

Redirecting front-end routes to Dashboard in Bolt CMS

I'm trying to redirect front end routes to the admin dashboard, as I'm using the Bolt installation as a REST API back end. Here's how I'm routing the content:
contentlink:
path: /{contenttypeslug}/{slug}
defaults: { _controller: 'Bolt\Controllers\Backend::dashboard' }
requirements:
contenttypeslug: 'Bolt\Controllers\Routing::getAnyContentTypeRequirement'
So, all I've done is use the dashboard controller. When I try to visit one of those routes, I get the following whoops error:
Twig_Error_Loader
Template "dashboard/dashboard.twig" is not defined ()
So for some reason it's not looking in the correct place for the template. Is there a way to correct this?
This looks like it's to do with the Twig path which is setup differently depending on whether there is a frontend or backend request.
you can always add a path to the Twig environment that Bolt uses with the following call:
$app['twig.loader.filesystem']->prependPath("/path/to/twig");
The path to the backend twig templates may vary but usually this will work.
$path = $app['resources']->getPath('app/view/twig');
$app['twig.loader.filesystem']->prependPath($path);

How to have Express routing work with Angular routing with HTML5 style URLs?

I would like to make an AngularJS app with HTML5-style URLs (i.e. with no # fragment in the URL). Thus in the routing controller module of my Angular app I've got something like the following:
angular.module('app').config(['$routeProvider','$locationProvider',function($routeProvider,$locationProvider) {
$locationProvider.html5Mode(true)
...
}
$routeProvider
.when('/1/:param', /* do something */)
.when('/2/:param', /* do something else */)
.when('/3/:param', /* do something else again*/);
A number of working examples like AngularFun don't use HTML5 mode. For a request like http://localhost:3005/#/1/foo, it's clear that
the http://localhost:3005/ part is handled server-side / by Express. Express happily serves our Angular-enabled index.html
the /1/foo route is handled client-side by Angular's router
Say our server.coffee looks, as standard, something like the below (we serve the static dist directory that contains our compiled, minified Angular sources:
express = require 'express'
routes = require './routes'
dir = "#{__dirname}/dist" # sources live here
port = process.env.PORT ? process.argv.splice(2)[0] ? 3005
app = express()
app.configure ->
app.use express.logger 'dev'
app.use express.bodyParser()
app.use express.methodOverride()
app.use express.errorHandler()
app.use express.static dir # serve the dist directory
app.use app.router
routes app, dir # extra custom routes
If we do use HTML5 mode, our URL http://localhost:3005/#/1/foo becomes http://localhost:3005/1/foo (no more hash #). This time, the entire URL is intercepted by Express, and it gets confused because we don't define routes other than /.
What we would really like to say is that the latter part of the URL (/1/foo) should be 'delegated' to Angular for handling. How can we say this?
It appears that it does all work. I didn't do that work myself, however. I relied on this skeleton project:
https://github.com/thanh-nguyen/angular-express-seed-coffee
This allows you a certain degree of control over which paths are handled by the client and which by the server.