Demo: https://cds-node-starter.herokuapp.com
Changelog: changelog.md
This repository provides a codebase that can be used to quickly build web pages or forms with a Government of Canada look-and-feel.
It provides the following functionality:
/en/
and /fr/
URL namespacesIt's setup with some sensible defaults and tech choices, such as:
empty
Github repo (must be empty)
git remote add upstream git@github.com:cds-snc/node-starter-app.git
git pull upstream master
git remote -v // ensure the remotes are setup properly
// you should see
origin git@github.com:cds-snc/your-repo.git (fetch)
origin git@github.com:cds-snc/your-repo.git (push)
upstream git@github.com:cds-snc/node-starter-app.git (fetch)
upstream git@github.com:cds-snc/node-starter-app.git (push)
npm install
npm run dev
There is a base set of SASS stylesheets included by default that provide a good base visual starting point.
TailwindCSS is included, but completely optional. If you don't like it, you can just remove the @tailwind directives in app.scss, remove the tailwind.scss customizations, and remove the tailwindcss plugin from postcss.config.js.
Webpack loads app.scss and imported sheets, runs them through PostCSS which parses SASS, sets up Tailwindcss, compiles, minimizes with cssnano, and applies autoprefixer. On production builds, everything gets passed through PurgeCSS to remove any unused classes to really reduce file size.
app.scss is where we recommend you place custom SASS or CSS rules.
Generate the route files
node ./bin/route.js create --route your_route_name
The created route directory by default contains the following files:
Register the route via routes.config.js
// config/routes.config.js
...
const routes = [
{ name: "your_route_name", path: "/your_route_name" },
];
...
Note: Delete unused route(s) directories as needed.
Redirects are handled via route.doRedirect()
. The doRedirect function will do a look up for the next route based on the routes config.
For cases where the redirect is not straight forward you can pass in a function, which can return a route name or a route object:
// routes.config.js
const routes = [
...
{ name: 'my-route', ..., skipTo: 'other-route' }
...
]
// my-route.controller.js
route.draw(app)
.post(..., route.doRedirect((req, res) => shouldSkip(req) ? route.skipTo : route.next))
Note that there is nothing specific about the name skipTo
: any key that is set in the routes configuration will be visible from the route
object the controller receives.
CSRF protection for forms is provided by csurf middleware.
Note that the CSRF token is passed to all templates through response.locals, ie:
// append csrfToken to all responses
app.use(function (req, res, next) {
res.locals.csrfToken = req.csrfToken()
next()
})
To successfully submit a form, you must include a CSRF token in a hidden field:
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
If using JS/Ajax, you can get the csrf token from the header meta tag included in the base template:
<meta name="csrf-token" content="{{ csrfToken }}">
The following is an example of using the Fetch API in the browser to post to the /personal
route with the CSRF token from the <meta>
tag on the page:
// - client.js - //
// Read the CSRF token from the <meta> tag
const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
// Make a request using the Fetch API
fetch('/process', {
credentials: 'same-origin', // <-- includes cookies in the request
headers: {
'CSRF-Token': token // <-- is the csrf token as a header
},
method: 'POST',
body: {
favoriteColor: 'blue'
}
})
Text on pages is supplied via content IDs, and the localization framework provides the correct text depending on the locale. All form macros (such as textInput
) receive these IDs for labels or other content.
{% block content %}
<h1>{{ __('personal.title') }}</h1>
<div>
<p>{{ __('personal.intro') }}</p>
</div>
<form method="post">
{{ textInput('fullname', 'form.fullname') }}
</form>
{% endblock %}
// locales/en.json
{
// ...
"personal.title": "Personal Information",
"personal.intro": "Intro copy goes here",
"form.fullname": "Full name",
// ...
}
To mark fields showing as required you can pass required: true as an attribute
Template Engine
See views/_includes
Don't like the way it's setup -> it's an Express server so do your thing app.js
> node ./bin/cli.js routes
[ { name: 'sample', path: '/sample' },
{ name: 'start', path: '/start' },
{ name: 'personal', path: '/personal' },
{ name: 'confirmation', path: '/confirmation' } ]
cloudbuild.yaml
will not work out of the box, so it will need to be tweaked as well as the permissions set correctly in GCP. This link explains the required steps to set up Cloud Run properly.app
levelRoutes should act like a plugin. i.e. Project B has a page you need, copy the route directory and add that route to your config.
This project is based on the orginal code https://github.com/cds-snc/cra-claim-tax-benefits it was born out of wanting to use that code as a base without the need to remove the unused parts everytime a new project is started.
See:
Starter Cloud Build / Cloud Run setup is in place if you prefer to deploy via GCP see
notification-demo-service
which is setup to deploy using a tag.
Démo: https://cds-node-starter.herokuapp.com
Journal des modifications: changelog.md
Ce dépôt fournit un code base pouvant être utilisé pour créer rapidement des pages Web ou des formulaires Web à l’image du Gouvernement du Canada.
Le dépôt est configuré avec des valeurs par défaut et des choix technologiques pratiques comme:
git remote add upstream git@github.com:cds-snc/node-starter-app.git
git pull upstream master
git remote -v // ensure the remotes are setup properly
// you should see
origin git@github.com:cds-snc/your-repo.git (fetch)
origin git@github.com:cds-snc/your-repo.git (push)
upstream git@github.com:cds-snc/node-starter-app.git (fetch)
upstream git@github.com:cds-snc/node-starter-app.git (push)
npm install
npm run dev
Il existe un ensemble de base de feuilles de styles qui est inclus par défaut et qui fournit un bon point de départ pour un visuel de base.
TailwindCSS est inclus, mais est tout à fait facultatif. Si vous ne l’aimez pas, vous n’avez qu’à supprimer les directives @tailwind dans app.scss, les personnalisations tailwind.scss, et le plugiciel tailwindcss dans postcss.config.js.
Webpack charge app.scss et les feuilles importées, les exécute par l’intermédiaire de PostCSS qui analyse les fichiers SASS, configure Tailwindcss, compile, minifie avec CSSnano et applique Autoprefixer. Sur les versions de production, tout passe par PurgeCSS pour éliminer les classes inutilisées et réduire véritablement la taille des fichiers.
app.scss est l’endroit où nous recommandons que vous placiez des règles SASS ou CSS personnalisées.
Générez les fichiers de route
node ./bin/route.js create --route your_route_name
Le répertoire de la route créé contient par défaut les fichiers suivants :
Enregistrez la route via routes.config.js
// config/routes.config.js
...
const routes = [
{ name: "your_route_name", path: "/your_route_name" },
];
...
Remarque : Supprimez au besoin les répertoires de routes inutilisés.
Les redirections sont gérées avec route.doRedirect(). La fonction doRedirect recherche la route suivante en fonction de la configuration des routes.
Pour les situations où la redirection n’est pas simple, vous pouvez introduire une fonction qui retourne un nom de route ou un objet de route :
// routes.config.js
const routes = [
...
{ name: 'my-route', ..., skipTo: 'other-route' }
...
]
// my-route.controller.js
route.draw(app)
.post(..., route.doRedirect((req, res) => shouldSkip(req) ? route.skipTo : route.next))
La protection contre la falsification de requête intersites (CSRF)[https://github.com/expressjs/csurf] est fournie par l’intergiciel csurf.
Notez que le jeton CSRF est transmis à tous les modèles par l’intermédiaire de response.locals, c’est-à-dire :
// append csrfToken to all responses
app.use(function (req, res, next) {
res.locals.csrfToken = req.csrfToken()
next()
})
Pour réussir à soumettre un formulaire, vous devez inclure un jeton CSRF dans un champ caché :
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
Si vous utilisez JS/Ajax, vous pouvez obtenir le jeton CSRF à partir de la balise d’en-tête meta incluse dans le modèle de base :
<meta name="csrf-token" content="{{ csrfToken }}">
L’exemple suivant est un exemple d’utilisation de l’API Fetch pour publier sur la route /personal avec le jeton CSRF provenant de la balise sur la page :
// Read the CSRF token from the <meta> tag
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
// Make a request using the Fetch API
fetch('/process', {
credentials: 'same-origin', // <-- includes cookies in the request
headers: {
'CSRF-Token': token // <-- is the csrf token as a header
},
method: 'POST',
body: {
favoriteColor: 'blue'
}
})
Le texte dans les pages est fourni par des ID.
block variables
-var title = __('personal.title')
block content
h1 #{title}
div
p #{__('personal.intro')}
form(method='post')
// locales/en.json
"personal.title": "Personal Information",
"personal.intro": "Intro copy goes here",
"form.fullname": "Full name",
Pour indiquer que des champs sont requis, vous pouvez faire passer required: true comme attribut.
Consultez views/_includes
Vous n’aimez pas la configuration actuelle -> c’est un serveur Express, donc faites ce que vous souhaitez app.js
> node ./bin/cli.js routes
[ { name: 'sample', path: '/sample' },
{ name: 'start', path: '/start' },
{ name: 'personal', path: '/personal' },
{ name: 'confirmation', path: '/confirmation' } ]
La version et le déploiement par défaut actuels se font via GCP Cloud Build et Cloud Run. Le cloudbuild.yaml n’est pas une solution toute faite, donc il devra être ajusté, tout comme les permissions correctement définies dans GCP. Ce lien explique les étapes requises pour bien configurer Cloud Run.
Les routes devraient agir comme un plugiciel. Ex. : Soit le Projet B, qui a une page dont vous avez besoin. Copiez le répertoire de la route et ajoutez cette route à votre configuration.
Le projet est basé sur le code original de https://github.com/cds-snc/cra-claim-tax-benefits et est né de la volonté d’utiliser ce code comme base, sans avoir à éliminer les parties inutilisées chaque fois qu’un nouveau projet débute.
Consultez :
La configuration pour Starter Cloud Build / Cloud Run est établie; si vous préférez déployer via GCP consultez notification-demo-service, qui est configuré pour faire des déploiements en utilisant une balise.