Skip to content

Commit aa60275

Browse files
chore: sync upstream (#404)
* chore: sync upstream * fix: repo references * fix: fixes with upstream * chore: continue sync upstream * fix: lint and snapshots * fix: deps and lint Co-authored-by: niftylettuce <[email protected]>
1 parent d22782e commit aa60275

File tree

142 files changed

+7479
-8762
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+7479
-8762
lines changed

README.md

+200-5
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ These microservices are preconfigured for security, performance, and graceful re
6767

6868
* Webapp server → [web.js](template/web.js)
6969
* API server → [api.js](template/api.js)
70-
* Job scheduler → [bull.js](template/bull.js)
70+
* Job scheduler → [bree.js](template/bree.js)
7171
* Proxy server → [proxy.js](template/proxy.js)
7272

7373
### Front-end
@@ -302,6 +302,181 @@ We strongly recommend using [SemaphoreCI][], [PM2][], and [Digital Ocean][do] fo
302302
303303
5. If you specify an environment variable value for `AWS_CF_DOMAIN` and `NODE_ENV=production` is set then your assets will need to be published to Amazon S3/Cloudfront. To do so run `npm start publish-assets` (or with yarn as `yarn start publish-assets`). This command automatically sets `NODE_ENV=production` for you as well via `cross-env`.
304304
305+
#### Provisioning
306+
307+
See the [ansible](ansible/) folder for our [Ansible][] configuration and playbooks, which we use to provision servers with.
308+
309+
We recommend you to install [yamllint][] and configure it in your editor while working with [Ansible][] playbooks.
310+
311+
Also note that [ansible-lint][] is a helpful linting tool you can use if you plan on making changes to playbooks. Note that our current playbooks have several existing lint errors.
312+
313+
First you must provision Ubuntu 18.04 LTS 64-bit server(s) using [Digital Ocean][digital-ocean], [Linode][], [Vultr][], or your host of choice. These newly provisioned server(s) should have your SSH key automatically added.
314+
315+
Follow the [Deployment](#deployment) guide below for automatic provisioning and deployment instructions.
316+
317+
##### Deployment
318+
319+
1. Set up host configuration by copying the `hosts.yml` file template:
320+
321+
```sh
322+
cp ansible/playbooks/templates/hosts.yml hosts.yml
323+
```
324+
325+
2. Edit this configuration and update the file with your newly created server aliases and IP addresses. You can add more than one host to each group if you are setting up load balancing. Refer to the [Naming Convention](#naming-convention) documentation for our recommended approach to server alias naming. Note that this file is automatically ignored by git. If you have a private repository and would like to commit this, then remove `hosts.yml` from the root `.gitignore` file.
326+
327+
```sh
328+
vim hosts.yml
329+
```
330+
331+
3. Set up environment configuration by copying the `env` file template:
332+
333+
```sh
334+
cp ansible/playbooks/templates/env .env.production
335+
```
336+
337+
4. Edit this configuration and reference the official [Lad][] documentation for a list of all available environment variables (or see [.env.defaults](.env.defaults)). **You will need to open this file in your preferred editor** and set the values for any fields containing `TODO`, whereby you replace `TODO` with the appropriate value. Preserve double quotes where they are already defined.
338+
339+
```sh
340+
vim .env.production
341+
```
342+
343+
5. Generate [pm2][] [ecosystem files][ecosystem-files] using our automatic template generator. We created an [ansible-playbook.js](ansible-playbook.js) which loads the `.env.production` environment variables rendered with [@ladjs/env][] into `process.env`, which then gets used in the playbooks. This is a superior, simple, and the only known dotenv approach we know of in Ansible. Newly created `ecosystem-api.json`, `ecosystem-bree.json`, `ecosystem-web.json` files will now be created for you in the root of the repository. If you ever more add or change IP addresses, you can simply re-run this command.
344+
345+
```sh
346+
node ansible-playbook ansible/playbooks/ecosystem.yml -l 'localhost'
347+
```
348+
349+
6. Set up the web and API server(s) (see [patterns and ansible-playbook flags docs](https://docs.ansible.com/ansible/latest/user_guide/intro_patterns.html#patterns-and-ansible-playbook-flags) if you need help). If you completely (or partially) run this playbook (or any others below), then the second time you try to run it may not succeed. This is because we prevent root user access through security hardening. To workaround this, run the same command but without `-e 'ansible_user=root'` appended as it will default to the `devops` user created.
350+
351+
```sh
352+
node ansible-playbook ansible/playbooks/http.yml -e 'ansible_user=root' -l 'http'
353+
```
354+
355+
7. Set up the Bree server(s):
356+
357+
```sh
358+
node ansible-playbook ansible/playbooks/bree.yml -e 'ansible_user=root' -l 'bree'
359+
```
360+
361+
8. Set up the Redis server:
362+
363+
```sh
364+
node ansible-playbook ansible/playbooks/redis.yml -e 'ansible_user=root' -l 'redis'
365+
```
366+
367+
9. Set up the Mongo server:
368+
369+
```sh
370+
node ansible-playbook ansible/playbooks/mongo.yml -e 'ansible_user=root' -l 'mongo'
371+
```
372+
373+
10. Set up GitHub deployment keys for all the servers. Note that the `deployment-keys` directory is ignored from git, so if you have a private repository and wish to commit it, then remove `deployment-keys` from the `.gitignore` file.
374+
375+
```sh
376+
node ansible-playbook ansible/playbooks/deployment-keys.yml -l 'http:bree'
377+
```
378+
379+
11. Go to your repository "Settings" page on GitHub, click on "Deploy keys", and then add a deployment key for each servers' deployment key copied to the `deployment-keys` directory. If you're on macOS, you can use the `pbcopy` command to copy each file's contents to your clipboard. Use tab completion for speed, and replace the server names and paths with yours:
380+
381+
```sh
382+
cat deployment-keys/api-1-li-dal.forwardemail.net.pub | pbcopy
383+
384+
#
385+
# NOTE: repeat the above command for all servers
386+
# and after running the command, it will copy
387+
# the key to your clipboard for you to paste as
388+
# a new deploy key (make sure to use read-only access)
389+
#
390+
```
391+
392+
12. Set up PM2 deployment directories on all the servers:
393+
394+
```sh
395+
pm2 deploy ecosystem-web.json production setup
396+
```
397+
398+
```sh
399+
pm2 deploy ecosystem-api.json production setup
400+
```
401+
402+
```sh
403+
pm2 deploy ecosystem-bree.json production setup
404+
```
405+
406+
13. Create a SSL certificate at [Namecheap][] (we recommend a 5 year wildcard certificate), set up the certificate, and download and extract the ZIP file with the certificate (emailed to you) to your computer. We do not recommend using tools like [LetsEncrypt][] and `certbot` due to complexity when you have (or scale to) a cluster of servers set up behind load balancers. In other words, we've tried approaches like `lsyncd` in combination with `crontab` for `certbot` renewals and automatic checking. Furthermore, using this exposes the server(s) to downtime as ports `80` and `443` may need to be shut down so that `certbot` can use them for certificate generation. This is not a reliable approach, and simply renewing certificates once a year is vastly simpler and also makes using load balancers trivial. Instead you can use a provider like [Namecheap][] to get a cheap SSL certificate, then run a few commands as we've documented below. This command will prompt you for an absolute file path to the certificates you downloaded. Renewed your certificate after 1 year? Simply follow this step again. Do not set a password on the certificate files. When using the `openssl` command (see Namecheap instructions), you need to use `*.example.com` with an asterisk followed by a period if you are registering a wildcard certificate.
407+
408+
```sh
409+
ansible-playbook ansible/playbooks/certificates.yml
410+
```
411+
412+
> **Important:** If you renew or change certificates in the future, then after running the previous command, you will subsequently need to reload the processes as such:
413+
414+
```sh
415+
#
416+
# NOTE: See the "Important" note above BEFORE running this command.
417+
# This command ONLY APPLIES for certificate renewals/changes.
418+
#
419+
pm2 deploy ecosystem-web.json production exec "pm2 reload web"
420+
pm2 deploy ecosystem-api.json production exec "pm2 reload api"
421+
```
422+
423+
14. (Optional) Create a Google application credentials profile file and store it locally. You only need this if you want to support automatic translation. The following command will prompt you for the absolute file path (e.g. `/path/to/client-profile.json`). See the [mandarin][] docs for more information.
424+
425+
```sh
426+
ansible-playbook ansible/playbooks/gapp-creds.yml -l 'http:bree'
427+
```
428+
429+
15. Copy the `.env.production` file and create an AWS config file on the servers:
430+
431+
```sh
432+
node ansible-playbook ansible/playbooks/env.yml -l 'http:bree'
433+
```
434+
435+
16. Run an initial deploy to all the servers:
436+
437+
```sh
438+
pm2 deploy ecosystem-web.json production
439+
```
440+
441+
```sh
442+
pm2 deploy ecosystem-api.json production
443+
```
444+
445+
```sh
446+
pm2 deploy ecosystem-bree.json production
447+
```
448+
449+
17. Save the process list on the servers so when if the server were to reboot, it will automatically boot back up the processes:
450+
451+
```sh
452+
pm2 deploy ecosystem-web.json production exec "pm2 save"
453+
```
454+
455+
```sh
456+
pm2 deploy ecosystem-api.json production exec "pm2 save"
457+
```
458+
459+
```sh
460+
pm2 deploy ecosystem-bree.json production exec "pm2 save"
461+
```
462+
463+
464+
16. Test by visiting your web and API server in your browser (click "proceed to unsafe" site and bypass certificate warning).
465+
466+
17. Configure your DNS records for the web and API server hostnames and respective IP addresses.
467+
468+
18. Test by visiting your web and API server in your browser (in an incognito window). There should not be any certificate warnings (similar to the one that occurred in step 15).
469+
470+
19. (Optional) Remove the local `.env.production` file for security purposes. If you do this, then make sure you have a backup, or securely back up off the server in the future before destroying the server.
471+
472+
```sh
473+
rm .env.production
474+
```
475+
476+
20. (Optional) Remove the local certificate files you downloaded locally and specified in step 11. If you do this, then make sure you have a backup, or securely back up off the server in the future before destroying the server.
477+
478+
21. Finished. If you need to deploy again, then push your changes to GitHub `master` branch and then follow step 14 again. We recommend you to read the [Ansible getting started guide][ansible-guide], as it provides you with insight into commands like `ansible all -a "echo hello"` which can be run across all or specific servers.
479+
305480
#### Tests
306481

307482
We use [ava][] and [nyc][] for testing and code coverage.
@@ -706,7 +881,7 @@ template
706881
│   │   └── uncaught.js
707882
│   ├── manifest.json
708883
│   └── robots.txt
709-
├── bull.js
884+
├── bree.js
710885
├── config
711886
│   ├── env.js
712887
│   ├── index.js
@@ -833,7 +1008,7 @@ If you are seeking permission to use these trademarks, then please [contact us](
8331008
[MIT](LICENSE) © [Nick Baugh](http://niftylettuce.com)
8341009

8351010

836-
##
1011+
##
8371012

8381013
<a href="#"><img src="media/lad-footer.png" alt="#" /></a>
8391014

@@ -927,8 +1102,6 @@ If you are seeking permission to use these trademarks, then please [contact us](
9271102

9281103
[mongoose]: http://mongoosejs.com
9291104

930-
[bull]: https://github.com/OptimalBits/bull
931-
9321105
[ladjs-auth]: https://github.com/ladjs/auth
9331106

9341107
[csrf-alternative]: https://scotthelme.co.uk/csrf-is-dead/
@@ -1024,3 +1197,25 @@ If you are seeking permission to use these trademarks, then please [contact us](
10241197
[cabinjs options]: https://github.com/cabinjs/axe#options
10251198

10261199
[certbot options]: https://certbot.eff.org/docs/using.html#id11
1200+
1201+
[ansible]: https://github.com/ansible/ansible
1202+
1203+
[yamllint]: https://github.com/adrienverge/yamllint
1204+
1205+
[ansible-lint]: https://github.com/ansible/ansible-lint
1206+
1207+
[digital-ocean]: https://m.do.co/c/2ffb8129b8d6
1208+
1209+
[linode]: https://www.linode.com/?r=a2840b36770c7020730251a5643428ddbf2e284e
1210+
1211+
[vultr]: https://www.vultr.com/?ref=7429848
1212+
1213+
[ecosystem-files]: https://pm2.keymetrics.io/docs/usage/application-declaration/
1214+
1215+
[@ladjs/env]: https://github.com/ladjs/env
1216+
1217+
[namecheap]: https://namecheap.com
1218+
1219+
[letsencrypt]: https://letsencrypt.org/
1220+
1221+
[ansible-guide]: https://docs.ansible.com/ansible/latest/user_guide/intro_getting_started.html

cli.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const cli = cac();
1515

1616
cli
1717
.command('<name>', 'Generate a new project')
18-
.action(name => {
18+
.action((name) => {
1919
const folderName = name;
2020
const targetPath = path.resolve(folderName);
2121
console.log(`> Generating project in ${targetPath}`);

package.json

+16-14
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"Shaun Warman <[email protected]> (https://shaunwarman.com/)"
2525
],
2626
"dependencies": {
27-
"cac": "^6.5.8",
27+
"@ladjs/browserslist-config": "^0.0.1",
28+
"cac": "^6.6.1",
2829
"camelcase": "^5.3.1",
2930
"github-username-regex": "^1.0.0",
3031
"is-email": "^1.0.0",
@@ -39,21 +40,22 @@
3940
"uppercamelcase": "^3.0.0"
4041
},
4142
"devDependencies": {
42-
"@commitlint/cli": "^8.3.5",
43-
"@commitlint/config-conventional": "^8.3.4",
44-
"ava": "^2.4.0",
45-
"codecov": "^3.6.5",
46-
"cross-env": "^6.0.3",
47-
"eslint": "^6.8.0",
43+
"@commitlint/cli": "^9.1.1",
44+
"@commitlint/config-conventional": "^9.1.1",
45+
"ava": "^3.11.0",
46+
"codecov": "^3.7.2",
47+
"cross-env": "^7.0.2",
48+
"eslint": "^7.5.0",
4849
"eslint-config-xo-lass": "^1.0.3",
49-
"eslint-plugin-compat": "^3.5.1",
50-
"husky": "^3.1.0",
51-
"lint-staged": "^10.1.4",
52-
"nyc": "^15.0.1",
53-
"remark-cli": "^8.0.0",
54-
"remark-preset-github": "^2.0.0",
50+
"eslint-plugin-compat": "^3.8.0",
51+
"eslint-plugin-no-smart-quotes": "^1.1.0",
52+
"husky": "^4.2.5",
53+
"lint-staged": "^10.2.11",
54+
"nyc": "^15.1.0",
55+
"remark-cli": "^8.0.1",
56+
"remark-preset-github": "^2.0.2",
5557
"supertest": "^4.0.2",
56-
"xo": "^0.25.3"
58+
"xo": "^0.32.1"
5759
},
5860
"engines": {
5961
"node": ">=10"

sao.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module.exports = {
2323
name: {
2424
message: 'What is the name of the new project',
2525
default: ':folderName:',
26-
validate: value => {
26+
validate: (value) => {
2727
if (process.env.NODE_ENV === 'test' && value === 'lad') return true;
2828
return isValidNpmName(value);
2929
}
@@ -48,19 +48,19 @@ module.exports = {
4848
message: 'What is your email (the author’s)',
4949
default: conf.get('init-author-email') || ':gitEmail:',
5050
store: true,
51-
validate: value => (isEmail(value) ? true : 'Invalid email')
51+
validate: (value) => (isEmail(value) ? true : 'Invalid email')
5252
},
5353
website: {
5454
message: 'What is your personal website (the author’s)',
5555
default: conf.get('init-author-url') || '',
5656
store: true,
57-
validate: value => (value === '' || isURL(value) ? true : 'Invalid URL')
57+
validate: (value) => (value === '' || isURL(value) ? true : 'Invalid URL')
5858
},
5959
username: {
6060
message: 'What is your GitHub username or organization',
6161
default: ':gitUser:',
6262
store: true,
63-
validate: value =>
63+
validate: (value) =>
6464
githubUsernameRegex.test(value) ? true : 'Invalid GitHub username'
6565
},
6666
repo: {
@@ -70,7 +70,7 @@ module.exports = {
7070
slug(answers.name)
7171
)}`;
7272
},
73-
validate: value =>
73+
validate: (value) =>
7474
isURL(value) &&
7575
value.indexOf('https://github.com/') === 0 &&
7676
value.lastIndexOf('/') !== value.length - 1
@@ -87,8 +87,8 @@ module.exports = {
8787
type: 'confirm',
8888
default: true
8989
},
90-
bull: {
91-
message: 'Do you need a job scheduler (@ladjs/bull)',
90+
bree: {
91+
message: 'Do you need a job scheduler (bree)',
9292
type: 'confirm',
9393
default: true
9494
},
@@ -101,7 +101,7 @@ module.exports = {
101101
message: 'Do you need automatic multi-lingual support',
102102
type: 'confirm',
103103
default: true,
104-
when: answers => answers.web || answers.api
104+
when: (answers) => answers.web || answers.api
105105
}
106106
},
107107
filters: {
@@ -120,9 +120,9 @@ module.exports = {
120120

121121
'web.js': 'web === true',
122122
'api.js': 'api === true',
123-
'bull.js': 'bull === true',
123+
'bree.js': 'bree === true',
124124
'proxy.js': 'proxy === true',
125-
'jobs/**': 'bull === true',
125+
'jobs/**': 'bree === true',
126126

127127
'test/config/snapshots/': false,
128128
'test/config/snapshots/**': false,
@@ -137,10 +137,10 @@ module.exports = {
137137
README: 'README.md',
138138
env: '.env'
139139
},
140-
post: async ctx => {
140+
post: async (ctx) => {
141141
ctx.gitInit();
142142

143-
// TODO: ctx.answers.bull
143+
// TODO: ctx.answers.bree
144144
// - remove from pkg
145145
// - remove config
146146
// - remove tests

0 commit comments

Comments
 (0)