cds-snc / node-starter-app

Quick start application setup.... because you have to start somewhere.
MIT License
5 stars 3 forks source link
discovery internal node-starter-app

Canada Web Forms Starter Repo for Node.js

Total alerts Language grade: JavaScript

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:

It's setup with some sensible defaults and tech choices, such as:

Cloning and pulling upstream changes

  1. Create an 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)

Install + Dev Mode

npm install
npm run dev

Custom styles, Sass, PostCSS, TailwindCSS, and PurgeCSS

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.

Adding Routes

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.

Form step redirects

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.

Form CSRF Protection

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'
  }
})

Locales

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",
// ...
}

Form Validation

To mark fields showing as required you can pass required: true as an attribute

Template Engine

Nunjucks

Common View Helpers

See views/_includes

Change configuration

Don't like the way it's setup -> it's an Express server so do your thing app.js

CLI

> node ./bin/cli.js routes
[ { name: 'sample', path: '/sample' },
  { name: 'start', path: '/start' },
  { name: 'personal', path: '/personal' },
  { name: 'confirmation', path: '/confirmation' } ]

Deployment

Goals

Routes 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.

What this project is not

Notes

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épôt de départ pour la création de formulaires Web du GC pour Node.js

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:

Cloner et tirer des modifications en amont

  1. Créez un dépôt GitHub empty (doit être vide)

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)

Install + Mode Dev

npm install
npm run dev

Styles personnalisés, Sass, PostCSS, TailwindCSS et PurgeCSS

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.

Ajouter des routes

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.

Redirections pour étapes de formulaire

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

Protection CSRF pour formulaires

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'
  }
})

Paramètres régionaux

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",

Validation de formulaire

Pour indiquer que des champs sont requis, vous pouvez faire passer required: true comme attribut.

Template Engine

Nunjucks

Aides de vue communes

Consultez views/_includes

Changer la configuration

Vous n’aimez pas la configuration actuelle -> c’est un serveur Express, donc faites ce que vous souhaitez app.js

Interface de ligne de commande (CLI)

> node ./bin/cli.js routes
[ { name: 'sample', path: '/sample' },
  { name: 'start', path: '/start' },
  { name: 'personal', path: '/personal' },
  { name: 'confirmation', path: '/confirmation' } ]

Déploiement

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.

Objectifs

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.

Ce que le projet n’est pas

Notes

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.