Skip to content

Commit fdcc56b

Browse files
committed
feat: fixed AWS creds to standards, improved email setup
1 parent c709909 commit fdcc56b

File tree

12 files changed

+354
-1134
lines changed

12 files changed

+354
-1134
lines changed

template/.env.defaults

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,20 @@ GOOGLE_CLIENT_SECRET=
5555
GOOGLE_CALLBACK_URL={{WEB_URL}}/auth/google/ok
5656
# used by `mandarin` translation package
5757
GOOGLE_TRANSLATE_KEY=
58-
# your AWS credentials from:
59-
# https://console.aws.amazon.com/s3/home
60-
AWS_IAM_KEY=
61-
AWS_IAM_SECRET=
62-
AWS_S3_BUCKET=
63-
AWS_CF_DI=
64-
AWS_CF_DOMAIN=
65-
AWS_ACCESS_KEY_ID={{AWS_IAM_KEY}}
66-
AWS_SECRET_ACCESS_KEY={{AWS_IAM_SECRET}}
6758
# your Postmark token from:
6859
# https//postmarkapp.com
6960
POSTMARK_API_TOKEN=
7061
# your CodeCov token from:
7162
# https://codecov.io
7263
CODECOV_TOKEN=
64+
# aws credentials
65+
# https://docs.aws.amazon.com/en_pv/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html
66+
# https://docs.aws.amazon.com/en_pv/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html
67+
# https://docs.aws.amazon.com/en_pv/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html
68+
AWS_PROFILE=
69+
AWS_S3_BUCKET=
70+
AWS_CLOUDFRONT_DOMAIN=
71+
AWS_CLOUDFRONT_DISTRIBUTION_ID=
7372

7473
#############
7574
## mongodb ##

template/.env.schema

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,20 @@ GOOGLE_CLIENT_SECRET=
5555
GOOGLE_CALLBACK_URL=
5656
# used by `mandarain` translation package
5757
GOOGLE_TRANSLATE_KEY=
58-
# your AWS credentials from:
59-
# https://console.aws.amazon.com/s3/home
60-
AWS_IAM_KEY=
61-
AWS_IAM_SECRET=
62-
AWS_S3_BUCKET=
63-
AWS_CF_DI=
64-
AWS_CF_DOMAIN=
65-
AWS_ACCESS_KEY_ID=
66-
AWS_SECRET_ACCESS_KEY=
6758
# your Postmark token from:
6859
# https//postmarkapp.com
6960
POSTMARK_API_TOKEN=
7061
# your CodeCov token from:
7162
# https://codecov.io
7263
CODECOV_TOKEN=
64+
# aws credentials
65+
# https://docs.aws.amazon.com/en_pv/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html
66+
# https://docs.aws.amazon.com/en_pv/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html
67+
# https://docs.aws.amazon.com/en_pv/sdk-for-javascript/v2/developer-guide/loading-node-credentials-json-file.html
68+
AWS_PROFILE=
69+
AWS_S3_BUCKET=
70+
AWS_CLOUDFRONT_DOMAIN=
71+
AWS_CLOUDFRONT_DISTRIBUTION_ID=
7372

7473
#############
7574
## mongodb ##

template/app/controllers/web/index.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
const titleize = require('titleize');
1+
const { extname } = require('path');
2+
23
const _ = require('lodash');
4+
const titleize = require('titleize');
35

4-
const support = require('./support');
5-
const auth = require('./auth');
66
const admin = require('./admin');
7+
const auth = require('./auth');
78
const myAccount = require('./my-account');
9+
const support = require('./support');
810

911
function breadcrumbs(ctx, next) {
12+
// return early if its not a pure path (e.g. ignore static assets)
13+
// and also return early if it's not a GET request
14+
// and also return early if it's an XHR request
15+
if (ctx.method !== 'GET' || extname(ctx.path) !== '') return next();
16+
1017
const breadcrumbs = _.compact(ctx.path.split('/')).slice(1);
1118
ctx.state.breadcrumbs = breadcrumbs;
1219
ctx.state.meta.title = ctx.request.t(

template/config/index.js

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ const path = require('path');
33
const Axe = require('axe');
44
const Boom = require('@hapi/boom');
55
const I18N = require('@ladjs/i18n');
6+
const _ = require('lodash');
67
const base64ToS3 = require('nodemailer-base64-to-s3');
78
const boolean = require('boolean');
89
const consolidate = require('consolidate');
910
const nodemailer = require('nodemailer');
10-
const strength = require('strength');
1111
const pino = require('pino');
12+
const manifestRev = require('manifest-rev');
13+
const strength = require('strength');
1214
const { Signale } = require('signale');
1315

1416
const pkg = require('../package');
1517
const env = require('./env');
16-
const utilities = require('./utilities');
17-
const polyfills = require('./polyfills');
18-
const phrases = require('./phrases');
1918
const meta = require('./meta');
19+
const phrases = require('./phrases');
20+
const polyfills = require('./polyfills');
21+
const utilities = require('./utilities');
2022

2123
const config = {
2224
// package.json
@@ -37,7 +39,14 @@ const config = {
3739
},
3840
send: env.SEND_EMAIL,
3941
juiceResources: {
40-
preserveImportant: true
42+
preserveImportant: true,
43+
preserveFontFaces: false,
44+
preserveMediaQueries: false,
45+
preserveKeyFrames: false,
46+
removeStyleTags: true,
47+
insertPreservedExtraCss: false,
48+
extraCss: false,
49+
preservePseudos: false
4150
}
4251
},
4352
logger: {
@@ -67,17 +76,11 @@ const config = {
6776
directory: path.join(__dirname, '..', 'locales')
6877
},
6978

70-
aws: {
71-
key: env.AWS_IAM_KEY,
72-
accessKeyId: env.AWS_IAM_KEY,
73-
secret: env.AWS_IAM_SECRET,
74-
secretAccessKey: env.AWS_IAM_SECRET,
75-
distributionId: env.AWS_CF_DI,
76-
domainName: env.AWS_CF_DOMAIN,
77-
params: {
78-
Bucket: env.AWS_S3_BUCKET
79-
}
80-
},
79+
// <https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#constructor-property>
80+
aws: {},
81+
82+
// build directory
83+
buildBase: 'build',
8184

8285
// templating
8386
views: {
@@ -180,6 +183,9 @@ const config = {
180183
lastLocaleField: 'last_locale'
181184
};
182185

186+
// set build dir based off build base dir name
187+
config.buildDir = path.join(__dirname, '..', config.buildBase);
188+
183189
// add lastLocale configuration path name to both email-templates and i18n
184190
config.i18n.lastLocaleField = config.lastLocaleField;
185191
config.email.lastLocaleField = config.lastLocaleField;
@@ -194,6 +200,16 @@ const i18n = new I18N({
194200
logger
195201
});
196202

203+
// add manifest helper for rev-manifest.json support
204+
config.manifest = path.join(config.buildDir, 'rev-manifest.json');
205+
config.views.locals.manifest = manifestRev({
206+
prepend:
207+
env.AWS_CLOUDFRONT_DOMAIN && env.NODE_ENV === 'production'
208+
? `//${env.AWS_CLOUDFRONT_DOMAIN}/`
209+
: '/',
210+
manifest: config.manifest
211+
});
212+
197213
// add pug filter for easy translation of nested blocks
198214
config.views.locals.filters.translate = function(...args) {
199215
return i18n.api.t(...args);
@@ -215,13 +231,6 @@ config.email.transport = nodemailer.createTransport({
215231
logger,
216232
debug: boolean(env.TRANSPORT_DEBUG)
217233
});
218-
config.email.transport.use(
219-
'compile',
220-
base64ToS3({
221-
cloudFrontDomainName: env.AWS_CF_DOMAIN,
222-
aws: config.aws
223-
})
224-
);
225234

226235
config.email.views = { ...config.views };
227236
config.email.views.root = path.join(__dirname, '..', 'emails');
@@ -231,4 +240,28 @@ config.email.juiceResources.webResources = {
231240
images: true
232241
};
233242

243+
config.email.transport.use(
244+
'compile',
245+
base64ToS3({
246+
aws: _.merge(config.aws, {
247+
params: {
248+
Bucket: env.AWS_S3_BUCKET
249+
}
250+
}),
251+
cloudFrontDomainName: env.AWS_CLOUDFRONT_DOMAIN,
252+
fallbackDir: path.join(config.buildDir, 'img', 'nodemailer'),
253+
fallbackPrefix: `${config.urls.web}/img/nodemailer/`,
254+
logger
255+
})
256+
);
257+
258+
if (
259+
!config.email.juiceResources.webResources.images ||
260+
env.NODE_ENV !== 'production'
261+
)
262+
config.email.views.locals.manifest = manifestRev({
263+
prepend: `${config.urls.web}/`,
264+
manifest: config.manifest
265+
});
266+
234267
module.exports = config;

template/emails/_footer.pug

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
footer.py-3.mt-3
22
.container.text-center
33
a(href=config.urls.web)
4-
img(src='/img/logo-square.svg', width=100, height=100, alt='').d-inline-block.align-middle
4+
img(src=manifest('img/logo-square.svg'), width=100, height=100, alt='').d-inline-block.align-middle
55
ul.m-0.p-0.text-muted.py-3
66
li.d-inline: small: a(href=`${config.urls.web}/support`)= t('Support')
77
li.d-inline.mx-1: small &bull;

template/emails/_nav.pug

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
nav.bg-light.border.border-top-0.border-left-0.border-right-0.navbar.navbar-light
22
a(href=config.urls.web).navbar-brand
3-
img(src='/img/logo-square.svg', width=30, height=30, alt='').d-inline-block.align-middle
3+
img(src=manifest('img/logo-square.svg'), width=30, height=30, alt='').d-inline-block.align-middle
44
= ' '
55
!= png2x({ text: config.appName, fontSize: 30, backgroundColor: '#f8f9fa', fontNameOrPath: 'Bitter Regular' })
66
.navbar-expand
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
extends ../layout
3+
4+
block content
5+
.container.mt-3
6+
.card
7+
h5.card-header Account information updated
8+
.card-body
9+
.card-text
10+
p= `${user[config.passport.fields.givenName] ? user[config.passport.fields.givenName] : t('Hello')},`
11+
p= t(`The following changes were made to your account:`)
12+
ul.list-group.list-group-flush
13+
each change in changes
14+
li.list-group-item= change
15+
p= t('If you did not intend for this information to be updated, then please reply to let us know.')
16+
a.btn.btn-lg.btn-block.btn-success(href=link, role="button")= t('Change your password')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
= `${emoji('white_check_mark')} ${user[config.passport.fields.givenName] ? `${user[config.passport.fields.givenName]}, ${t('your account information was updated')}` : t('Account information updated')}`

template/gulpfile.js

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
const path = require('path');
22

3+
const AWS = require('aws-sdk');
4+
const _ = require('lodash');
5+
const awscloudfront = require('gulp-awspublish-cloudfront');
36
const awspublish = require('gulp-awspublish');
47
const babelify = require('@ladjs/babelify');
58
const browserify = require('browserify');
69
const buffer = require('vinyl-buffer');
7-
const cloudfront = require('gulp-cloudfront');
810
const collapser = require('bundle-collapser/plugin');
911
const commonShake = require('common-shakeify');
1012
const cssnano = require('cssnano');
@@ -47,28 +49,36 @@ process.env.I18N_UPDATE_FILES = true;
4749

4850
const env = require('./config/env');
4951
const config = require('./config');
52+
const logger = require('./helpers/logger');
5053

5154
const PROD = config.env === 'production';
5255
const DEV = config.env === 'development';
5356
const TEST = config.env === 'test';
54-
5557
const staticAssets = [
5658
'assets/**/*',
5759
'!assets/css/**/*',
5860
'!assets/img/**/*',
5961
'!assets/js/**/*'
6062
];
61-
6263
const manifestOptions = {
6364
merge: true,
64-
base: 'build'
65+
base: config.buildBase
6566
};
6667

68+
// set aws logger
69+
AWS.config.logger = logger;
70+
6771
function publish() {
6872
// create a new publisher
69-
const publisher = awspublish.create(config.aws);
73+
const publisher = awspublish.create(
74+
_.merge(config.aws, {
75+
params: {
76+
Bucket: env.AWS_S3_BUCKET
77+
}
78+
})
79+
);
7080
return (
71-
src(['build/**/*', '!build/rev-manifest.json'])
81+
src([`${config.buildBase}/**/*`, `!${config.manifest}`])
7282
// gzip, Set Content-Encoding headers and add .gz extension
7383
.pipe(awspublish.gzip())
7484
// publisher will add Content-Length, Content-Type
@@ -83,7 +93,7 @@ function publish() {
8393
.pipe(publisher.cache())
8494
// print upload updates to console
8595
.pipe(awspublish.reporter())
86-
.pipe(cloudfront(config.aws))
96+
.pipe(awscloudfront(env.AWS_CLOUDFRONT_DISTRIBUTION_ID))
8797
);
8898
}
8999

@@ -105,12 +115,10 @@ function img() {
105115
use: [pngquant()]
106116
})
107117
)
108-
.pipe(dest('build'))
118+
.pipe(dest(config.buildBase))
109119
.pipe(gulpif(DEV, lr(config.livereload)))
110-
.pipe(
111-
gulpif(PROD, rev.manifest('build/rev-manifest.json', manifestOptions))
112-
)
113-
.pipe(gulpif(PROD, dest('build')));
120+
.pipe(gulpif(PROD, rev.manifest(config.manifest, manifestOptions)))
121+
.pipe(gulpif(PROD, dest(config.buildBase)));
114122
}
115123

116124
function scss() {
@@ -134,7 +142,7 @@ function css() {
134142
.pipe(
135143
postcss([
136144
fontMagician({
137-
hosted: [path.join(__dirname, 'build', 'fonts'), '/fonts']
145+
hosted: [path.join(__dirname, config.buildBase, 'fonts'), '/fonts']
138146
}),
139147
unprefix(),
140148
postcssPresetEnv(),
@@ -145,12 +153,10 @@ function css() {
145153
)
146154
.pipe(gulpif(PROD, rev()))
147155
.pipe(sourcemaps.write('./'))
148-
.pipe(dest('build'))
156+
.pipe(dest(config.buildBase))
149157
.pipe(gulpif(DEV, lr(config.livereload)))
150-
.pipe(
151-
gulpif(PROD, rev.manifest('build/rev-manifest.json', manifestOptions))
152-
)
153-
.pipe(gulpif(PROD, dest('build')));
158+
.pipe(gulpif(PROD, rev.manifest(config.manifest, manifestOptions)))
159+
.pipe(gulpif(PROD, dest(config.buildBase)));
154160
}
155161

156162
function xo() {
@@ -161,7 +167,7 @@ function xo() {
161167
}
162168

163169
function eslint() {
164-
return src('build/**/*.js', { since: lastRun(eslint) })
170+
return src(`${config.buildBase}/**/*.js`, { since: lastRun(eslint) })
165171
.pipe(
166172
gulpEslint({
167173
allowInlineConfig: false,
@@ -198,12 +204,10 @@ function js() {
198204
.pipe(gulpif(PROD, uglify()))
199205
.pipe(gulpif(PROD, rev()))
200206
.pipe(sourcemaps.write('./'))
201-
.pipe(dest('build'))
207+
.pipe(dest(config.buildBase))
202208
.pipe(gulpif(DEV, lr(config.livereload)))
203-
.pipe(
204-
gulpif(PROD, rev.manifest('build/rev-manifest.json', manifestOptions))
205-
)
206-
.pipe(gulpif(PROD, dest('build')));
209+
.pipe(gulpif(PROD, rev.manifest(config.manifest, manifestOptions)))
210+
.pipe(gulpif(PROD, dest(config.buildBase)));
207211
}
208212

209213
function remark() {
@@ -222,11 +226,11 @@ function static() {
222226
base: 'assets',
223227
allowEmpty: true,
224228
since: lastRun(static)
225-
}).pipe(dest('build'));
229+
}).pipe(dest(config.buildBase));
226230
}
227231

228232
function clean() {
229-
return del(['build']);
233+
return del([config.buildBase]);
230234
}
231235

232236
const build = series(

0 commit comments

Comments
 (0)