The website users Hexo
to generate the static content and a
Node.js
backend to handle the dynamic parts. The template system is
Handlebars
so sharing code between static and dynamic
is easier. The template is under /src/alexanderhill00001-theme/layout/
.
If you are running the node
backend locally (/src/server/index.js
)
all the requests will be handled by it, even the static ones. This is
not the case in production.
Dynamic routes are defined under /src/server/routes/
. Currently only
the search results and scanner are dynamic. Basically, each file is a
node
module that exports a function that receives an express
object
does all the required configuration:
const configure = (app) => {
app.get('/custompath', (req, res) => {
res.render('customtemplate');
});
};
module.exports = configure;
To add a new route, create a new module in that folder and then
require
it in index.js#configureRoutes
:
const configureRoutes = (app) => {
require('./routes/scanner.js')(app);
require('./routes/search.js')(app);
};
Most of the site’s content comes from the alexanderhill00001
project and
its documentation. The markdown of that project is copied directly into
the /docs
folder. When the site is built, the markdown is transformed
into html
using a custom hexo
template into the /dist
folder,
among other static resources.
The content of the live site is updated each time a change is made in the:
alexanderhill00001.github.io
repository/docs
directory and/or to the CHANGELOG.md
file from the webhint
repositoryTo achieve the latter, webhint
has the trigger-site-update.sh
script that triggers a build on webhint.io
if the
previous conditions are met.
The search on the website uses Algolia. The search index is updated each time there is a deployment.
The deployment process is fully automated:
master
branch build is triggered in Travis (e.g. by code
or documentation change)..travis/update-site.sh
.
This script will copy the required files for the deployment and push
them into a local Git on Azure in the staging environment.webhint
to make sure
everything is alrigh. If so, the code will get into production via
.travis/swap.sh
.Currently the website is deployed into an Azure App Service using IIS,
IISNode, and node
.
The way IIS handles the requests is as follows:
/search
and /scanner
) are routed to node
This is configured in the web.config
file in the rules section:
<rule name="static">
<match url="(?!scanner|search).*$" ignoreCase="true"/>
<action type="Rewrite" url="dist{REQUEST_URI}"/>
</rule>
<rule name="ScannerAndSearch" stopProcessing="true">
<match url="(?:scanner|search)(.*)$" ignoreCase="true"/>
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="src/server/index.js"/>
</rule>
[webhint.io
][webhint] uses a Let’s Encrypt
certificate installed using [Let’s Encrypt site extension][siteextension].
It gets renewed automatically before it’s expiration date. The official
installation guide was used to configure everything.
During the renovation of the certificate, the extension needs to write
a specific value into the .well-known
folder. To make this folder
accessible we use the following rule in web.config
:
<rule name="wellknown" stopProcessing="true">
<match url="^\.well-known.*" />
<action type="Rewrite" url="{REQUEST_URI}"/>
</rule>
A valid certificate needs to be in place for the renewal to work.
Otherwise you will have to disable the https
redirect.
[webhint.io
][webhint] is an https
only website. The redirects
happens in the following web.config
rule:
<rule name="Redirect to https" stopProcessing="true">
<match url="(.*)"/>
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true"/>
<add input="{WARMUP_REQUEST}" pattern="1" negate="true" />
<add input="{REQUEST_URI}" pattern="/api/version/" negate="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" appendQueryString="true"/>
</rule>
Note: If for whatever reason the certificate expires this rule will have to be commented out until a new one is in place.
IIS manages the custom error page via the following entries in web.config
:
<system.webServer>
<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
<remove statusCode="404" subStatusCode="-1"/>
<error statusCode="404" path="/dist/404.html" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="/dist/404.html" redirectMode="ResponseRewrite"/>
</system.web>