Deploy multiple sources to the same server (Part 3)

Post by Lưu Đại at 19-01-2024
After using the product for a couple hours, I notice 2 more problems: 
  • Problem 1: Each time I reload page which have path like /bo/{something} it return 404 error. 
  • Problem 2: Each time I deploy new code client did not automatically load new js and css file from server.

In order to solve problem 2, I've to force reload js css file each time another version is available => I have to solve Problem 1 first, otherwise it will return 404 when reload page. 

1. Each time I reload page which have path like /bo/{something} it return 404 error

  • Reason: This error occur because Vuejs app is only load js and css the first time user access page. And it doesn't have any built in mechanism to reload js and css file when those file changed. Plus not like SSR application, Vuejs render html by js so if user route to another page it'll not call server because it have all the information it need to render in its js file.
    When reload page vuejs app act like it's the first the request. So request /bo/abc come to server, it may not understand and return 404. Note that when routing it not make request to the server and only modify the url. 
  • Solution: Config nginx so each time this kind of request go to server it'll return index.html (config on nginx or another reserve proxy). (This is vuejs recommendation)

location / {
  try_files $uri $uri/ /index.html;
}
There's a problem, each time user reload nginx will go to the location above then it redirects them to default location which is Rails app. It may work fine if the server only have Vuejs app or Vuejs app's location is /. 
But since I config Rails location is /, I have to find another way to achieve it. 
After trying a few ideals, I come up with this
I separate location as below: 
= /bo: exact /bo required
^~ /bo/assets: this use to serve js css file since /bo/assets/xyz not allow to go directly to Vuejs app. 
^~ /bo/(.*\.(jpg|jpeg|png|gif)$): Same as above but its purpose is serve static file
~ /bo/*: Each time this kind of request hit server (the one may cause 404 error), it'll be redirected to /bo. For example /bo/abc will go to bo. 

  location = /bo {
    proxy_pass http://app_fe_service/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Ssl on;
    proxy_set_header X-Forwarded-Port 443;
    proxy_set_header X-Forwarded-Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_redirect off;
    client_max_body_size 23M;
  }

  location ^~ /bo/assets {
    rewrite /bo/assets/(.*)$ /assets/$1 break;
    proxy_pass http://app_fe_service;
  }

  location ~* /bo/(.*\.(jpg|jpeg|png|gif)$) {
    rewrite /bo/(.*)$ /$1 break;
    proxy_pass http://app_fe_service;
  }

  location ~ /bo/* {
    rewrite /bo/* /bo last;
  }
Done now I can reload page without 404 error 💪

2. Each time I deploy new code client did not automatically load new js and css file from server

In Rails MVC each time a new version of js, css built, Rails may tell client by update the response header to the newest hash version of js, css. The client will read this header and compare it with the version it has, if they are different client will delete the current file and fetch newest files.
image.png

I will take the ideal and apply it into my project but instead of generate the version automatically, I will do it manually. Sorry for the laziness 🥹. Each time I change css or js that required client reload js and css file I will change the ClientVersion and attach ClientVersion to each response header. 
This is the middleware: 
Have to add new header to ExposeHeader too so Vuejs can access this value
image.png

In Vuejs, I use axios interceptor to handle the logic
Sorry about the messy code. I use interceptor to handle another logic too. 
And it doneee