How to collect all static assets (js, css) used by discourse

I’m trying to setup CDN for my forum. since China banned Fastly and lots of CDN servers, so I can’t use them to speed up my forum. After doing some research, I turn to Jsdelivr, which can serve static files from npm or Github repo.

I know I can use DISCOURSE_CDN_URL in app.yml to set up the URL. But before that, I have to collect all the static files (js, CSS) from my forum then upload it to Github so Jsdelivr can access them. I found the static files actually located inside the docker container:

/var/www/discourse/app/assets

So I’m thinking of copying all the files from this path every time after I rebuild the app then upload them to Github, I guess this idea should work, but the actual request contains a long hash name which I don’t know how to get it. For instance,

    ember_jquery.js will become
    ember_jquery-8ef4e572f0bf9485e6ef9a35f088729735f82434cf495fbcd5acedefbcddb363.js

So my question is

  1. How to generate the hash file name?
  2. Any other way to collect the static asserts?
1 Like

It’s a SHA1 hash of the file contents. It’s generated here:
https://github.com/discourse/discourse/blob/master/lib/tasks/assets.rake

The compiled versions that get used are put in /var/www/discourse/public/assets/.

If you add a stanza to your app.yml you can perform an action after the container is rebuilt. For example, to upload to S3 we use something similar to:

hooks:
  after_assets_precompile:
    - exec:
        cd: $home
        cmd:
          - sudo -E -u discourse bundle exec rake s3:upload_assets

In your case, replace the rake task with the commands you run to push the compiled assets to Github when the container is built.

3 Likes

Thank you @schleifer. Your reply is very helpful. However, I still have some follow up questions:

  1. Where I can find the document for hooks like after_assets_precompile? I searched Docker document as well as discourse Github repo but I can’t find any information about it. Do I have to set up the hooks named after_assets_precompile by myself (How to do this?) or it’s already set up by discourse like after_code?

  2. IIUC, my pseudocode would look like

     hooks:
       after_assets_precompile:
         - exec:
             cd: $home
             cmd:
               - enter to the docker container
               - cd to `/var/www/discourse/public/assets/`
               - git add, commit and push to Github repo
    

Thank you.

That’s a good question, but I don’t have a good answer for you.

The configuration system used inside the container is:
https://github.com/discourse/pups

The README there describes how the hooks work generally. The specific hooks available to use depend on how your instance is setup – the main app.yml will have includes one of more template. In most cases, one of those is web.template.yml:

https://github.com/discourse/discourse_docker/blob/master/templates/web.template.yml

That’s where the assets_precompile hook is defined (on [L159] as I type this). Per the pups documentation, you can run other commands before or after that completes with before_assets_precompile and after_assets_precompile respectively.

The commands are run inside the docker container, so you don’t have to do anything for that.

The cd: directive can point right to /var/www/discourse/public/assets/ and each line in cmd gets run from that directory. (Warning: each line in a cmd array is run in a separate shell, like in a Makefile).

I’m not sure if the contents of /var/www/discourse/public/assets/ would persist between builds, so you might have to create the local git repository and force push every time. The “more correct” solution might be to define a docker volume in app.yml for the assets directory on a docker volume defined in app.yml like the others and persist outside the container.

2 Likes

Thank you so much. I also found that some static assets (like CSS) are put in /var/www/discourse/tmp/stylesheet-cache.But there are two other problems:

  1. Users upload assets like avatar also serve by the CDN after setting up DISCOURSE_CDN_URL. However, push the upload assets to Github after every time user upload seems not a great solution.
  2. When discourse requesting user_avatar like 2_2.png. It will 1) split the file name, 2) do some check, 3) calculate the real file name using hashing. If I need to serve user avatar I have to implement the same logic myself since there is no file named 2_2.png.

My final solution is simple. 1) I add Nginx before my forum like this post. 2) Collect all the static assets from the above path and push them to Github. 3) In Nginx config file, add some rules like

location ~/(stylesheets|assets) {
    return 301 https://cdn.jsdelivr.net/gh/my_github/my_repo/$request_uri;
}

Thank you again @schleifer

1 Like