UNPKG

lad

Version:

Lad is the best Node.js framework. Made by a former Express TC and Koa team member.

326 lines (293 loc) 9.46 kB
const path = require('path'); const Axe = require('axe'); const Boom = require('@hapi/boom'); const _ = require('lodash'); const base64ToS3 = require('nodemailer-base64-to-s3'); const consolidate = require('consolidate'); const manifestRev = require('manifest-rev'); const ms = require('ms'); const nodemailer = require('nodemailer'); const zxcvbn = require('zxcvbn'); const { boolean } = require('boolean'); const pkg = require('../package'); const env = require('./env'); const filters = require('./filters'); const i18n = require('./i18n'); const loggerConfig = require('./logger'); const meta = require('./meta'); const phrases = require('./phrases'); const utilities = require('./utilities'); const config = { // package.json pkg, // server env: env.NODE_ENV, urls: { web: env.WEB_URL, api: env.API_URL }, // app supportRequestMaxLength: env.SUPPORT_REQUEST_MAX_LENGTH, email: { message: { from: env.EMAIL_DEFAULT_FROM }, send: env.SEND_EMAIL, juiceResources: { preserveImportant: true, preserveFontFaces: false, preserveMediaQueries: false, preserveKeyFrames: false, removeStyleTags: true, insertPreservedExtraCss: false, extraCss: false, preservePseudos: false }, lastLocaleField: 'last_locale', i18n: { ...i18n, autoReload: false, updateFiles: false, syncFiles: false } }, logger: loggerConfig, appName: env.APP_NAME, appColor: env.APP_COLOR, twitter: env.TWITTER, i18n, // <https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#constructor-property> aws: {}, // build directory buildBase: 'build', // templating views: { // root is required by `koa-views` root: path.join(__dirname, '..', 'app', 'views'), // These are options passed to `koa-views` // <https://github.com/queckezz/koa-views> // They are also used by the email job rendering options: { extension: 'pug', map: {}, engineSource: consolidate }, // A complete reference of options for Pug (default): // <https://pugjs.org/api/reference.html> locals: { // Even though pug deprecates this, we've added `pretty` // in `koa-views` package, so this option STILL works // <https://github.com/queckezz/koa-views/pull/111> pretty: env.NODE_ENV === 'development', cache: env.NODE_ENV !== 'development', // debug: env.NODE_ENV === 'development', // compileDebug: env.NODE_ENV === 'development', ...utilities, filters } }, // user fields whose account updates create an action (e.g. email) accountUpdateFields: [ 'passport.fields.otpEnabled', 'passport.fields.givenName', 'passport.fields.familyName', 'passportLocalMongoose.usernameField', 'userFields.apiToken' ], // user fields (change these if you want camel case or whatever) userFields: { accountUpdates: 'account_updates', fullEmail: 'full_email', apiToken: 'api_token', otpRecoveryKeys: 'otp_recovery_keys', resetTokenExpiresAt: 'reset_token_expires_at', resetToken: 'reset_token', changeEmailTokenExpiresAt: 'change_email_token_expires_at', changeEmailToken: 'change_email_token', changeEmailNewAddress: 'change_email_new_address', hasSetPassword: 'has_set_password', hasVerifiedEmail: 'has_verified_email', pendingRecovery: 'pending_recovery', verificationPinExpiresAt: 'verification_pin_expires_at', verificationPinSentAt: 'verification_pin_sent_at', verificationPin: 'verification_pin', verificationPinHasExpired: 'verification_pin_has_expired', welcomeEmailSentAt: 'welcome_email_sent_at', twoFactorReminderSentAt: 'two_factor_reminder_sent_at' }, // dynamic otp routes otpRoutePrefix: '/otp', otpRouteLoginPath: '/login', // dynamic otp routes loginOtpRoute: '/otp/login', // login route loginRoute: '/login', // verification verifyRoute: '/verify', verificationPinTimeoutMs: ms(env.VERIFICATION_PIN_TIMEOUT_MS), verificationPinEmailIntervalMs: ms(env.VERIFICATION_PIN_EMAIL_INTERVAL_MS), verificationPin: { length: 6, type: 'numeric' }, // reset token resetTokenTimeoutMs: ms(env.RESET_TOKEN_TIMEOUT_MS), // change email token changeEmailTokenTimeoutMs: ms(env.CHANGE_EMAIL_TOKEN_TIMEOUT_MS), changeEmailLimitMs: ms(env.CHANGE_EMAIL_LIMIT_MS), // @ladjs/passport configuration (see defaults in package) // <https://github.com/ladjs/passport> passport: { fields: { // you may want to make this "full_name" instead displayName: 'display_name', // you could make this "first_name" givenName: 'given_name', // you could make this "last_name" familyName: 'family_name', avatarURL: 'avatar_url', googleProfileID: 'google_profile_id', googleAccessToken: 'google_access_token', googleRefreshToken: 'google_refresh_token', githubProfileID: 'github_profile_id', githubAccessToken: 'github_access_token', githubRefreshToken: 'github_refresh_token', otpToken: 'otp_token', otpEnabled: 'otp_enabled' }, google: { accessType: 'offline', prompt: 'consent', scope: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile' ] }, github: { scope: ['user:email'] } }, // passport-local-mongoose options // <https://github.com/saintedlama/passport-local-mongoose> passportLocalMongoose: { usernameField: 'email', passwordField: 'password', attemptsField: 'login_attempts', lastLoginField: 'last_login_at', usernameLowerCase: true, limitAttempts: true, maxAttempts: env.NODE_ENV === 'development' ? Number.POSITIVE_INFINITY : 5, digestAlgorithm: 'sha256', encoding: 'hex', saltlen: 32, iterations: 25000, keylen: 512, passwordValidator: (password, fn) => { if (env.NODE_ENV === 'development') return fn(); const { score } = zxcvbn(password); fn(score < 3 ? Boom.badRequest(phrases.INVALID_PASSWORD_STRENGTH) : null); }, errorMessages: { MissingPasswordError: phrases.PASSPORT_MISSING_PASSWORD_ERROR, AttemptTooSoonError: phrases.PASSPORT_ATTEMPT_TOO_SOON_ERROR, TooManyAttemptsError: phrases.PASSPORT_TOO_MANY_ATTEMPTS_ERROR_, NoSaltValueStoredError: phrases.PASSPORT_NO_SALT_VALUE_STORED_ERROR, IncorrectPasswordError: phrases.PASSPORT_INCORRECT_PASSWORD_ERROR, IncorrectUsernameError: phrases.PASSPORT_INCORRECT_USERNAME_ERROR, MissingUsernameError: phrases.PASSPORT_MISSING_USERNAME_ERROR, UserExistsError: phrases.PASSPORT_USER_EXISTS_ERROR } }, // passport callback options passportCallbackOptions: { successReturnToOrRedirect: '/my-account', failureRedirect: '/register', successFlash: true, failureFlash: true }, // store IP address // <https://github.com/ladjs/store-ip-address> storeIPAddress: { ip: 'ip', lastIps: 'last_ips' }, // field name for a user's last locale // (this gets re-used by email-templates and @ladjs/i18n; see below) lastLocaleField: 'last_locale' }; // set dynamic login otp route config.loginOtpRoute = `${config.otpRoutePrefix}${config.otpRouteLoginPath}`; // set build dir based off build base dir name config.buildDir = path.join(__dirname, '..', config.buildBase); // meta support for SEO config.meta = meta(config); // add i18n api to views 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: '/', manifest: config.srimanifest }); // add global `config` object to be used by views config.views.locals.config = _.pick(config, [ 'appColor', 'appName', 'env', 'lastLocaleField', 'loginRoute', 'maxForwardedAddresses', 'otpRoutePrefix', 'passport', 'passportCallbackOptions', 'passportLocalMongoose', 'pkg', 'recordPrefix', 'storeIPAddress', 'supportRequestMaxLength', 'twitter', 'urls', 'userFields', 'verificationPin', 'verifyRoute' ]); // add `views` to `config.email` config.email.transport = nodemailer.createTransport({ // you can use any transport here // but we use postmarkapp.com by default // <https://nodemailer.com/transports/> service: 'postmark', auth: { user: env.POSTMARK_API_TOKEN, pass: env.POSTMARK_API_TOKEN }, logger, debug: boolean(env.TRANSPORT_DEBUG) }); config.email.views = { ...config.views }; config.email.views.root = path.join(__dirname, '..', 'emails'); config.email.juiceResources.webResources = { relativeTo: config.buildDir, images: true }; config.email.transport.use( 'compile', base64ToS3({ aws: _.merge({}, config.aws, { params: { Bucket: env.AWS_S3_BUCKET } }), cloudFrontDomainName: env.AWS_CLOUDFRONT_DOMAIN, fallbackDir: path.join(config.buildDir, 'img', 'nodemailer'), fallbackPrefix: `${config.urls.web}/img/nodemailer/`, logger }) ); if ( !config.email.juiceResources.webResources.images || env.NODE_ENV !== 'production' ) config.email.views.locals.manifest = manifestRev({ prepend: `${config.urls.web}/`, manifest: config.manifest }); module.exports = config;