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 nodeThis 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>