Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"is-valid-npm-name": "^0.0.5",
"npm-conf": "^1.1.3",
"sao": "0.x",
"semver": "^7.1.3",
"semver": "^7.3.2",
"speakingurl": "^14.0.1",
"superb": "^4.0.0",
"update-notifier": "^4.1.0",
Expand All @@ -48,8 +48,8 @@
"eslint-config-xo-lass": "^1.0.3",
"eslint-plugin-compat": "^3.5.1",
"husky": "^3.1.0",
"lint-staged": "^10.1.1",
"nyc": "^15.0.0",
"lint-staged": "^10.1.3",
"nyc": "^15.0.1",
"remark-cli": "^8.0.0",
"remark-preset-github": "^1.0.0",
"supertest": "^4.0.2",
Expand Down
58 changes: 35 additions & 23 deletions template/app/views/layout.pug
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,28 @@ html(lang=locale).h-100

//- css file
block stylesheets
link(rel="stylesheet", href=manifest('css/app.css'))
if config.env === 'production'
link(rel="stylesheet", href=manifest('css/app.css') integrity=manifest('css/app.css', 'integrity'))
else
link(rel="stylesheet", href=manifest('css/app.css'))

body(role='document', class=['/register', '/login', config.verifyRoute].includes(ctx.pathWithoutLocale) ? 'pt-0' : '').d-flex.flex-column.h-100

//- spinner
block spinner
include spinner/spinner

//- navigation
block navigation
include _nav

//- body
main(role='main').flex-shrink-0
block body

//- footer
block footer
include _footer

block scripts
//- flash messaging (with koa-better-flash and sweetalert2)
Expand Down Expand Up @@ -102,31 +123,22 @@ html(lang=locale).h-100
};

//- polyfill environment
script(defer, src=`https://polyfill.io/v3/polyfill${config.env === 'production' ? '.min' : ''}.js?features=${polyfills.join(',')}`)
script(src=`https://polyfill.io/v3/polyfill${config.env === 'production' ? '.min' : ''}.js?features=${polyfills.join(',')}`)

//- factor bundle (common shared assets across all files)
script(defer, src=manifest('js/factor-bundle.js'))
if config.env === 'production'
script(src=manifest('factor-bundle.js') integrity=manifest('factor-bundle.js', 'integrity'))
else
script(src=manifest('factor-bundle.js'))

//- uncaught (handles errors, similar to TraceKit but with CabinJS + StackTrace.JS)
script(defer, src=manifest('js/uncaught.js'))
if config.env === 'production'
script(src=manifest('uncaught.js') integrity=manifest('uncaught.js', 'integrity'))
else
script(src=manifest('uncaught.js'))

//- scripts
script(defer, src=manifest('js/core.js'))

body(role='document', class=['/register', '/login', config.verifyRoute].includes(ctx.pathWithoutLocale) ? 'pt-0' : '').d-flex.flex-column.h-100

//- spinner
block spinner
include spinner/spinner

//- navigation
block navigation
include _nav

//- body
main(role='main').flex-shrink-0
block body

//- footer
block footer
include _footer
if config.env === 'production'
script(src=manifest('core.js') integrity=manifest('core.js', 'integrity'))
else
script(src=manifest('core.js'))
45 changes: 30 additions & 15 deletions template/app/views/my-account/security.pug
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ block body
input(type="hidden", name="_csrf", value=ctx.csrf)
button(type='submit').btn.btn-primary.btn-block
i.fa.fa-download
= t(' Download')
= ' '
= t('Download')
.col-sm.offset-sm-1
button(type='button', data-toggle="clipboard", data-clipboard-target="#two-factor-recovery-keys").btn.btn-secondary.btn-block
i.fa.fa-clipboard
= t(' Copy')
= ' '
= t('Copy')
.modal-footer
p.text-muted.text-center.font-weight-bold: small= t('Store these in a safe place; they will let you recover your account, if necessary.')
button(data-toggle='modal', data-target='#modal-enable-2fa-verify', data-dismiss='modal', type='button').btn.btn-primary.btn-lg.float-right= t('Continue')
.form-check
input#recovery-keys-stored(type='checkbox', name="recovery-keys-stored", value='true' data-toggle='collapse' data-target='#recovery-key-submit')
label(for='recovery-keys-stored')= t('I have stored these recovery keys in a safe place')
#recovery-key-submit.collapse
button(data-toggle='modal', data-target='#modal-enable-2fa-verify', data-dismiss='modal', type='button').btn.btn-primary.btn-lg.float-right= t('Continue')
#modal-enable-2fa-verify(tabindex='-1', role='dialog').modal.fade
.modal-dialog(role='document')
.modal-content
Expand All @@ -44,22 +49,32 @@ block body
.modal-body
form(action=l('/my-account/setup-2fa'), method='POST').ajax-form.confirm-prompt
input(type="hidden", name="_csrf", value=ctx.csrf)
label(for='two-factor-step-one')
b= t('Step 1: ')
= t('Install an authenticator application on your mobile device.')
label(for='two-factor-step-two')
b= t('Step 2: ')
= t('Scan the following QR code in your authenticator app.')
img(src=qrcode, width=250, height=250, alt="").mx-auto.d-block
hr
.input-group.input-group-sm.floating-label.form-group
input(type='text', readonly, value=user[config.passport.fields.twoFactorToken]).form-control#two-factor-token
label(for='two-factor-token')= t('Two-Factor Token')
.input-group-append
button(type='button', data-toggle="clipboard", data-clipboard-target="#two-factor-token").btn.btn-primary
i.fa.fa-clipboard
= ' '
= t('Copy')
hr
p.lead.text-center.font-weight-bold= t('Enter the token generated from your app below:')
label(for='two-factor-step-three')
b= t('Step 3: ')
= t('Enter the token generated from your app below:')
.form-group.floating-label
input(type='text', name='token', required, placeholder=' ').form-control.form-control-lg#input-token
label(for='input-token') Verification Token
p.text-muted.text-center.text-uppercase.font-weight-bold: small: mark= t('Hint: Scan the QR code or use the URI above if necessary')
a.card-link.text-primary(data-toggle='collapse' data-target='#two-factor-token')= t('Can’t scan the QR code? Follow alternative steps')
#two-factor-token.collapse
hr
p.text-secondary= t('Scan the following QR code in your authenticator app.')
.input-group.input-group-sm.floating-label.form-group
input(type='text', readonly, value=user[config.passport.fields.twoFactorToken]).form-control#two-factor-token
.input-group-append
button(type='button', data-toggle="clipboard", data-clipboard-target="#two-factor-token").btn.btn-primary
i.fa.fa-clipboard
= ' '
= t('Copy')
hr
button(type='submit').btn.btn-lg.btn-block.btn-primary= t('Continue')
.container-fluid.py-3
.row.mt-1
Expand Down
9 changes: 3 additions & 6 deletions template/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,7 @@ const config = {
passwordValidator: (password, fn) => {
if (env.NODE_ENV === 'development') return fn();
const { score } = zxcvbn(password);
fn(
score < 3
? Boom.badRequest(phrases.INVALID_PASSWORD_STRENGTH)
: null
);
fn(score < 3 ? Boom.badRequest(phrases.INVALID_PASSWORD_STRENGTH) : null);
},
errorMessages: {
MissingPasswordError: phrases.PASSPORT_MISSING_PASSWORD_ERROR,
Expand Down Expand Up @@ -221,12 +217,13 @@ const logger = new Axe(config.logger);

// add manifest helper for rev-manifest.json support
config.manifest = path.join(config.buildDir, 'rev-manifest.json');
config.srimanifest = path.join(config.buildDir, 'sri-manifest.json');
config.views.locals.manifest = manifestRev({
prepend:
env.AWS_CLOUDFRONT_DOMAIN && env.NODE_ENV === 'production'
? `//${env.AWS_CLOUDFRONT_DOMAIN}/`
: '/',
manifest: config.manifest
manifest: config.srimanifest
});

// add global `config` object to be used by views
Expand Down
45 changes: 20 additions & 25 deletions template/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const Mandarin = require('mandarin');
const _ = require('lodash');
const awscloudfront = require('gulp-awspublish-cloudfront');
const awspublish = require('gulp-awspublish');
const babel = require('gulp-babel');
const browserify = require('browserify');
const collapser = require('bundle-collapser/plugin');
const cssnano = require('cssnano');
Expand Down Expand Up @@ -37,6 +36,7 @@ const sourcemaps = require('gulp-sourcemaps');
const stylelint = require('stylelint');
const terser = require('gulp-terser');
const unassert = require('gulp-unassert');
const revSri = require('gulp-rev-sri');
const { lastRun, watch, series, parallel, src, dest } = require('gulp');

// explicitly set the compiler in case it were to change to dart
Expand Down Expand Up @@ -120,6 +120,7 @@ function img() {
.pipe(dest(config.buildBase))
.pipe(gulpif(DEV, lr(config.livereload)))
.pipe(gulpif(PROD, rev.manifest(config.manifest, manifestOptions)))
.pipe(gulpif(PROD, revSri({ base: config.buildBase })))
.pipe(gulpif(PROD, dest(config.buildBase)));
}

Expand Down Expand Up @@ -155,6 +156,7 @@ function css() {
.pipe(dest(config.buildBase))
.pipe(gulpif(DEV, lr(config.livereload)))
.pipe(gulpif(PROD, rev.manifest(config.manifest, manifestOptions)))
.pipe(gulpif(PROD, revSri({ base: config.buildBase })))
.pipe(gulpif(PROD, dest(config.buildBase)));
}

Expand All @@ -180,41 +182,36 @@ function eslint() {
async function bundle() {
// make build/js folder for compile task
await makeDir(path.join(config.buildBase, 'js'));
const ws = fs.createWriteStream(
path.join(config.buildBase, 'js', 'factor-bundle.js')
);
const paths = await globby('**/*.js', { cwd: 'assets/js' });
const b = browserify({
entries: paths.map(string => `assets/js/${string}`),
debug: true
});
return (
b
.plugin(collapser)
.plugin('factor-bundle', {
outputs: paths.map(string => path.join(config.buildBase, 'js', string))
})
.bundle()
// .bundle((err, buffer) => {
.pipe(
fs.createWriteStream(
path.join(config.buildBase, 'js', 'factor-bundle.js')
)
)
);
return b
.plugin(collapser)
.plugin('factor-bundle', {
outputs: paths.map(string => path.join(config.buildBase, 'js', string))
})
.bundle()
.pipe(ws)
.on('finish', compile);
}

async function compile() {
return src('build/js/**/*.js', {
since: lastRun(compile)
})
function compile() {
return src('build/js/**/*.js', { since: lastRun(compile) })
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(envify(env))
.pipe(unassert())
.pipe(babel())
.pipe(gulpif(PROD, terser()))
.pipe(gulpif(PROD, rev()))
.pipe(sourcemaps.write('./'))
.pipe(dest(config.buildBase))
.pipe(gulpif(DEV, lr(config.livereload)))
.pipe(gulpif(PROD, rev.manifest(config.manifest, manifestOptions)))
.pipe(gulpif(PROD, revSri({ base: config.buildBase })))
.pipe(gulpif(PROD, dest(config.buildBase)));
}

Expand All @@ -241,8 +238,6 @@ function clean() {
return del([config.buildBase]);
}

const js = series(bundle, compile);

async function markdown() {
const mandarin = new Mandarin({ i18n, logger });
const graceful = new Graceful({ redisClients: [mandarin.redisClient] });
Expand All @@ -254,13 +249,13 @@ const build = series(
clean,
parallel(
...(TEST ? [] : [xo, remark]),
parallel(img, static, markdown, series(scss, css), series(js, eslint))
parallel(img, static, markdown, series(scss, css), series(bundle, eslint))
)
);

module.exports = {
build,
js,
bundle,
publish,
markdown,
watch: () => {
Expand All @@ -269,7 +264,7 @@ module.exports = {
watch(Mandarin.DEFAULT_PATTERNS, markdown);
watch('assets/img/**/*', img);
watch('assets/css/**/*.scss', series(scss, css));
watch('assets/js/**/*.js', series(xo, js, eslint));
watch('assets/js/**/*.js', series(xo, bundle, eslint));
watch('app/views/**/*.pug', pug);
watch(staticAssets, static);
},
Expand Down
20 changes: 19 additions & 1 deletion template/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,23 @@
"Password was invalid.": "Password was invalid.",
"You can only request a password reset every 30 minutes. Please try again %s.": "You can only request a password reset every 30 minutes. Please try again %s.",
"Your support request has been sent successfully. You should hear from us soon. Thank you!": "Your support request has been sent successfully. You should hear from us soon. Thank you!",
"You have reached the limit for sending support requests. Please try again.": "You have reached the limit for sending support requests. Please try again."
"You have reached the limit for sending support requests. Please try again.": "You have reached the limit for sending support requests. Please try again.",
"Email address or password is incorrect.": "Email address or password is incorrect.",
"Dashboard &#124; <span class=\"notranslate\">Lad</span>": "Dashboard &#124; <span class=\"notranslate\">Lad</span>",
"Access your Lad account dashboard": "Access your Lad account dashboard",
"Verify email &#124; <span class=\"notranslate\">Lad</span>": "Verify email &#124; <span class=\"notranslate\">Lad</span>",
"Verify your Lad email": "Verify your Lad email",
"An email verification pin has been sent to your email address.": "An email verification pin has been sent to your email address.",
"Verify email": "Verify email",
"Enter the verification pin sent to your email address.": "Enter the verification pin sent to your email address.",
"Please enter a 6 digit verification pin.": "Please enter a 6 digit verification pin.",
"Verification pin": "Verification pin",
"Continue": "Continue",
"Didn't receive the email?": "Didn't receive the email?",
"Resend now": "Resend now",
"Use this verification pin:": "Use this verification pin:",
"This pin expires within 5 minutes.": "This pin expires within 5 minutes.",
"Verify now": "Verify now",
"If you did not submit this request, then please reply to let us know.": "If you did not submit this request, then please reply to let us know.",
"Verification pin:": "Verification pin:"
}
20 changes: 19 additions & 1 deletion template/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,23 @@
"Password was invalid.": "Password was invalid.",
"You can only request a password reset every 30 minutes. Please try again %s.": "You can only request a password reset every 30 minutes. Please try again %s.",
"Your support request has been sent successfully. You should hear from us soon. Thank you!": "Your support request has been sent successfully. You should hear from us soon. Thank you!",
"You have reached the limit for sending support requests. Please try again.": "You have reached the limit for sending support requests. Please try again."
"You have reached the limit for sending support requests. Please try again.": "You have reached the limit for sending support requests. Please try again.",
"Email address or password is incorrect.": "Email address or password is incorrect.",
"Dashboard &#124; <span class=\"notranslate\">Lad</span>": "Dashboard &#124; <span class=\"notranslate\">Lad</span>",
"Access your Lad account dashboard": "Access your Lad account dashboard",
"Verify email &#124; <span class=\"notranslate\">Lad</span>": "Verify email &#124; <span class=\"notranslate\">Lad</span>",
"Verify your Lad email": "Verify your Lad email",
"An email verification pin has been sent to your email address.": "An email verification pin has been sent to your email address.",
"Verify email": "Verify email",
"Enter the verification pin sent to your email address.": "Enter the verification pin sent to your email address.",
"Please enter a 6 digit verification pin.": "Please enter a 6 digit verification pin.",
"Verification pin": "Verification pin",
"Continue": "Continue",
"Didn't receive the email?": "Didn't receive the email?",
"Resend now": "Resend now",
"Use this verification pin:": "Use this verification pin:",
"This pin expires within 5 minutes.": "This pin expires within 5 minutes.",
"Verify now": "Verify now",
"If you did not submit this request, then please reply to let us know.": "If you did not submit this request, then please reply to let us know.",
"Verification pin:": "Verification pin:"
}
20 changes: 19 additions & 1 deletion template/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,23 @@
"Password was invalid.": "Password was invalid.",
"You can only request a password reset every 30 minutes. Please try again %s.": "You can only request a password reset every 30 minutes. Please try again %s.",
"Your support request has been sent successfully. You should hear from us soon. Thank you!": "Your support request has been sent successfully. You should hear from us soon. Thank you!",
"You have reached the limit for sending support requests. Please try again.": "You have reached the limit for sending support requests. Please try again."
"You have reached the limit for sending support requests. Please try again.": "You have reached the limit for sending support requests. Please try again.",
"Email address or password is incorrect.": "Email address or password is incorrect.",
"Dashboard &#124; <span class=\"notranslate\">Lad</span>": "Dashboard &#124; <span class=\"notranslate\">Lad</span>",
"Access your Lad account dashboard": "Access your Lad account dashboard",
"Verify email &#124; <span class=\"notranslate\">Lad</span>": "Verify email &#124; <span class=\"notranslate\">Lad</span>",
"Verify your Lad email": "Verify your Lad email",
"An email verification pin has been sent to your email address.": "An email verification pin has been sent to your email address.",
"Verify email": "Verify email",
"Enter the verification pin sent to your email address.": "Enter the verification pin sent to your email address.",
"Please enter a 6 digit verification pin.": "Please enter a 6 digit verification pin.",
"Verification pin": "Verification pin",
"Continue": "Continue",
"Didn't receive the email?": "Didn't receive the email?",
"Resend now": "Resend now",
"Use this verification pin:": "Use this verification pin:",
"This pin expires within 5 minutes.": "This pin expires within 5 minutes.",
"Verify now": "Verify now",
"If you did not submit this request, then please reply to let us know.": "If you did not submit this request, then please reply to let us know.",
"Verification pin:": "Verification pin:"
}
Loading