diff --git a/.gitignore b/.gitignore index 4fca50862..a9e79b65a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ *.log .DS_Store .zedstate -config/_config.yml.bak +config/*.bak Dockme.yml -lint.html npm-debug.log .nyc_output/ coverage/ diff --git a/app.js b/app.js index 38c1b6473..4caae6217 100644 --- a/app.js +++ b/app.js @@ -1,15 +1,15 @@ 'use strict'; -const path = require('path'); -const express = require('express'); -const uuidv4 = require('uuid/v4'); -const semver = require('semver'); +const path = require('path'); +const express = require('express'); +const uuidv4 = require('uuid/v4'); +const semver = require('semver'); // constants -const ENV = process.env; -const NODE_ENV = ENV.NODE_ENV || 'development'; -const PUBLIC_DIR = path.join(__dirname, 'public'); -const STATIC_OPTS = { +const ENV = process.env; +const NODE_ENV = ENV.NODE_ENV || 'development'; +const PUBLIC_DIR = path.join(__dirname, 'public'); +const STATIC_OPTS = { maxAge: '1y', lastModified: true, etag: false @@ -28,12 +28,11 @@ const staticify = require('staticify')(PUBLIC_DIR, { sendOptions: STATIC_OPTS }); -const helpers = require('./lib/helpers.js'); -const CSP = require('./config/helmet-csp.js'); -const routes = require('./routes'); +const config = require('./config'); +const helpers = require('./lib/helpers.js'); +const routes = require('./routes'); -const config = helpers.getConfig(); -const app = express(); +const app = express(); // all environments app.set('views', path.join(__dirname, '/views/')); @@ -130,7 +129,7 @@ app.use(helmet.hsts({ app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' })); app.use(helmet.contentSecurityPolicy({ - directives: CSP, + directives: config.CSP, // This module will detect common mistakes in your directives and throw errors // if it finds any. To disable this, enable "loose mode". diff --git a/bin/www.js b/bin/www.js index 3ba9141ee..780377150 100644 --- a/bin/www.js +++ b/bin/www.js @@ -2,9 +2,7 @@ const http = require('http'); const app = require('../app.js'); -const helpers = require('../lib/helpers.js'); - -const config = helpers.getConfig(); +const config = require('../config'); app.set('port', process.env.PORT || config.port || 3000); diff --git a/config/_app.yml b/config/_app.yml new file mode 100644 index 000000000..c6d284167 --- /dev/null +++ b/config/_app.yml @@ -0,0 +1,43 @@ +port: 3333 +theme: 1 +siteurl: 'https://www.bootstrapcdn.com' +authors: + - name: Justin Dorfman + twitter: jdorfman + url: 'https://twitter.com/jdorfman' + work: + text: Sticker Mule + url: 'https://www.stickermule.com/' + - name: Joshua Mervine + twitter: mervinej + url: 'https://twitter.com/mervinej' + work: + text: Heroku + url: 'https://www.heroku.com/' + - name: XhmikosR + twitter: XhmikosR + url: 'https://xhmikosr.io/' +description: 'The recommended CDN for Bootstrap, Font Awesome and Bootswatch.' +favicon: + uri: /assets/img/favicons/favicon.ico +stylesheet: + - name: iubenda + uri: /assets/css/privacy_policy.css +javascript: + - name: jquery + uri: 'https://code.jquery.com/jquery-3.3.1.slim.min.js' + sri: sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo + - name: clipboardjs + uri: /assets/js/vendor/clipboard.min.js + - name: main + uri: /assets/js/main.js +redirects: + - name: legacy + from: /legacy/ + to: /legacy/bootstrap/ + - name: alpha + from: /alpha/ + to: / + - name: beta + from: /beta/ + to: / diff --git a/config/_extras.yml b/config/_extras.yml new file mode 100644 index 000000000..e73ef7c80 --- /dev/null +++ b/config/_extras.yml @@ -0,0 +1,133 @@ +showcase: + - name: Microsoft Education + img: /assets/img/showcase/microsoft-education.png + lib: Font Awesome 4.3.0 + url: 'https://www.microsoft.com/en-us/education/default.aspx' + - name: New York Post + img: /assets/img/showcase/new-york-post.png + lib: Font Awesome 4.0.3 + url: 'https://nypost.com/' + - name: NBC News + img: /assets/img/showcase/nbc-news.png + lib: Font Awesome 4.0.3 + url: 'https://www.nbcnews.com/' + - name: EA Help + img: /assets/img/showcase/ea-help.png + lib: Font Awesome 3.2.1 + url: 'https://help.ea.com/en/' + - name: Stanford University + img: /assets/img/showcase/stanford.png + lib: Font Awesome 4.3.0 + url: 'https://www.stanford.edu/' + - name: Go Doc + img: /assets/img/showcase/godoc-org.png + lib: Bootstrap 3.3.1 + url: 'https://godoc.org/' + - name: Mozilla Webmaker API + img: /assets/img/showcase/mozilla-webmaker.png + lib: Bootstrap 3.0.3 + url: 'https://api.webmaker.org/docs' + - name: New York University + img: /assets/img/showcase/nyu.png + lib: Font Awesome 4.2.0 + url: 'https://www.nyu.edu/' + - name: Cracked + img: /assets/img/showcase/cracked.png + lib: 'Bootstrap 3.3.2, Font Awesome 4.3.0' + url: 'http://www.cracked.com/' + - name: HP Support + img: /assets/img/showcase/hp-support.png + lib: Font Awesome 4.1.0 + url: 'https://support.hp.com/us-en' + - name: Monster + img: /assets/img/showcase/monster.png + lib: Font Awesome 4.2.0 + url: 'https://www.monster.com/' + - name: New Relic + img: /assets/img/showcase/new-relic.png + lib: Font Awesome 4.2.0 + url: 'https://newrelic.com/' + - name: Nasdaq + img: /assets/img/showcase/nasdaq.png + lib: Font Awesome 4.4.0 + url: 'https://www.nasdaq.com/' + - name: Creative Commons + img: /assets/img/showcase/creative-commons.png + lib: Bootstrap 3.2.0 + url: 'https://creativecommons.org/' + - name: Udacity + img: /assets/img/showcase/udacity.png + lib: Bootstrap 3.0.0 + url: 'https://www.udacity.com/course/website-performance-optimization--ud884' + - name: AdRoll + img: /assets/img/showcase/llorda.png + lib: Font Awesome 4.5.0 + url: 'https://help.adroll.com/hc/en-us' + - name: Kubernetes Bootcamp + img: /assets/img/showcase/kubernetes-bootcamp.png + lib: Font Awesome 4.5.0 + url: 'https://kubernetesbootcamp.github.io/kubernetes-bootcamp/' +integrations: + - name: Adobe Dreamweaver + img: /assets/img/integrations/dreamweaver.png + plat: Adobe Dreamweaver + url: 'https://helpx.adobe.com/dreamweaver/using/bootstrap.html' + - name: freeCodeCamp + img: /assets/img/integrations/freecodecamp.png + plat: Node.js + url: 'https://www.freecodecamp.org/challenges/use-responsive-design-with-bootstrap-fluid-containers' + - name: Bootstrap 3 Snippets + img: /assets/img/integrations/atom-bootstrap3.png + plat: Atom + url: 'https://atom.io/packages/atom-bootstrap3' + - name: Bootstrap 3 + img: /assets/img/integrations/drupal-bootstrap3.png + plat: Drupal + url: 'https://www.drupal.org/project/bootstrap' + - name: Freight + img: /assets/img/integrations/sentry-freight.png + plat: Python + url: 'https://github.com/getsentry/freight' + - name: Bootstrap NBM + img: /assets/img/integrations/bootstrap-nbm.png + plat: NetBeans + url: 'https://www.davidsalter.co.uk/bootstrap-cdn-plugin-for-netbeans-release-1-1-0/' + - name: BootstrapCDN WP + img: /assets/img/integrations/bootstrapcdn-wp.png + plat: WordPress + url: 'https://wordpress.org/plugins/bootstrapcdn/' + - name: Light Bootstrap Dashboard + img: /assets/img/integrations/light-bootstrap-dashboard.png + plat: WebPlatform + url: 'https://www.creative-tim.com/product/light-bootstrap-dashboard' + - name: Radix Theme + img: /assets/img/integrations/radix-theme-drupal.png + plat: Drupal + url: 'https://www.drupal.org/project/radix' + - name: Bootstrap 3 Sublime Plugin + img: /assets/img/integrations/bs3-sublime-plugin.png + plat: Sublime + url: 'https://github.com/JasonMortonNZ/bs3-sublime-plugin' + - name: Bootstrap 3 Jade Sublime Plugin + img: /assets/img/integrations/bs3-jade-sublime-plugin.png + plat: Sublime + url: 'https://github.com/rs459/bootstrap3-jade-sublime-plugin' + - name: Bootstrap Editor and Builder + img: /assets/img/integrations/bootply.png + plat: Bootply + url: 'https://www.bootply.com/' + - name: React-Bootstrap + img: /assets/img/integrations/react-bootstrap.png + plat: React + url: 'https://react-bootstrap.github.io/' +friends: + - name: ScaleScale + url: 'https://www.scalescale.com/' + - name: Bootstrap + url: 'https://getbootstrap.com/' + - name: Sticker Mule + url: 'https://www.stickermule.com/' + - name: nixCraft + url: 'https://www.cyberciti.biz/' + - name: Fort Awesome + url: 'https://fortawesome.com/' diff --git a/config/_config.yml b/config/_files.yml similarity index 80% rename from config/_config.yml rename to config/_files.yml index 8ca44783e..ae5f4d9b2 100644 --- a/config/_config.yml +++ b/config/_files.yml @@ -1,46 +1,3 @@ -port: 3333 -theme: 1 -siteurl: 'https://www.bootstrapcdn.com' -authors: - - name: Justin Dorfman - twitter: jdorfman - url: 'https://twitter.com/jdorfman' - work: - text: Sticker Mule - url: 'https://www.stickermule.com/' - - name: Joshua Mervine - twitter: mervinej - url: 'https://twitter.com/mervinej' - work: - text: Heroku - url: 'https://www.heroku.com/' - - name: XhmikosR - twitter: XhmikosR - url: 'https://xhmikosr.io/' -description: 'The recommended CDN for Bootstrap, Font Awesome and Bootswatch.' -favicon: - uri: /assets/img/favicons/favicon.ico -stylesheet: - - name: iubenda - uri: /assets/css/privacy_policy.css -javascript: - - name: jquery - uri: 'https://code.jquery.com/jquery-3.3.1.slim.min.js' - sri: sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo - - name: clipboardjs - uri: /assets/js/vendor/clipboard.min.js - - name: main - uri: /assets/js/main.js -redirects: - - name: legacy - from: /legacy/ - to: /legacy/bootstrap/ - - name: alpha - from: /alpha/ - to: / - - name: beta - from: /beta/ - to: / bootstrap: - version: 4.1.1 current: true @@ -400,136 +357,3 @@ fontawesome: - version: 3.2.1 stylesheet: 'https://stackpath.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css' stylesheetSri: sha384-5WSMKsEjlK1hO/E+0ERtEmsujy8NFgEb15UOB5phg+1xk2zTO0Dd71qJ+7yD1QFN -showcase: - - name: Microsoft Education - img: /assets/img/showcase/microsoft-education.png - lib: Font Awesome 4.3.0 - url: 'https://www.microsoft.com/en-us/education/default.aspx' - - name: New York Post - img: /assets/img/showcase/new-york-post.png - lib: Font Awesome 4.0.3 - url: 'https://nypost.com/' - - name: NBC News - img: /assets/img/showcase/nbc-news.png - lib: Font Awesome 4.0.3 - url: 'https://www.nbcnews.com/' - - name: EA Help - img: /assets/img/showcase/ea-help.png - lib: Font Awesome 3.2.1 - url: 'https://help.ea.com/en/' - - name: Stanford University - img: /assets/img/showcase/stanford.png - lib: Font Awesome 4.3.0 - url: 'https://www.stanford.edu/' - - name: Go Doc - img: /assets/img/showcase/godoc-org.png - lib: Bootstrap 3.3.1 - url: 'https://godoc.org/' - - name: Mozilla Webmaker API - img: /assets/img/showcase/mozilla-webmaker.png - lib: Bootstrap 3.0.3 - url: 'https://api.webmaker.org/docs' - - name: New York University - img: /assets/img/showcase/nyu.png - lib: Font Awesome 4.2.0 - url: 'https://www.nyu.edu/' - - name: Cracked - img: /assets/img/showcase/cracked.png - lib: 'Bootstrap 3.3.2, Font Awesome 4.3.0' - url: 'http://www.cracked.com/' - - name: HP Support - img: /assets/img/showcase/hp-support.png - lib: Font Awesome 4.1.0 - url: 'https://support.hp.com/us-en' - - name: Monster - img: /assets/img/showcase/monster.png - lib: Font Awesome 4.2.0 - url: 'https://www.monster.com/' - - name: New Relic - img: /assets/img/showcase/new-relic.png - lib: Font Awesome 4.2.0 - url: 'https://newrelic.com/' - - name: Nasdaq - img: /assets/img/showcase/nasdaq.png - lib: Font Awesome 4.4.0 - url: 'https://www.nasdaq.com/' - - name: Creative Commons - img: /assets/img/showcase/creative-commons.png - lib: Bootstrap 3.2.0 - url: 'https://creativecommons.org/' - - name: Udacity - img: /assets/img/showcase/udacity.png - lib: Bootstrap 3.0.0 - url: 'https://www.udacity.com/course/website-performance-optimization--ud884' - - name: AdRoll - img: /assets/img/showcase/llorda.png - lib: Font Awesome 4.5.0 - url: 'https://help.adroll.com/hc/en-us' - - name: Kubernetes Bootcamp - img: /assets/img/showcase/kubernetes-bootcamp.png - lib: Font Awesome 4.5.0 - url: 'https://kubernetesbootcamp.github.io/kubernetes-bootcamp/' -integrations: - - name: Adobe Dreamweaver - img: /assets/img/integrations/dreamweaver.png - plat: Adobe Dreamweaver - url: 'https://helpx.adobe.com/dreamweaver/using/bootstrap.html' - - name: freeCodeCamp - img: /assets/img/integrations/freecodecamp.png - plat: Node.js - url: 'https://www.freecodecamp.org/challenges/use-responsive-design-with-bootstrap-fluid-containers' - - name: Bootstrap 3 Snippets - img: /assets/img/integrations/atom-bootstrap3.png - plat: Atom - url: 'https://atom.io/packages/atom-bootstrap3' - - name: Bootstrap 3 - img: /assets/img/integrations/drupal-bootstrap3.png - plat: Drupal - url: 'https://www.drupal.org/project/bootstrap' - - name: Freight - img: /assets/img/integrations/sentry-freight.png - plat: Python - url: 'https://github.com/getsentry/freight' - - name: Bootstrap NBM - img: /assets/img/integrations/bootstrap-nbm.png - plat: NetBeans - url: 'https://www.davidsalter.co.uk/bootstrap-cdn-plugin-for-netbeans-release-1-1-0/' - - name: BootstrapCDN WP - img: /assets/img/integrations/bootstrapcdn-wp.png - plat: WordPress - url: 'https://wordpress.org/plugins/bootstrapcdn/' - - name: Light Bootstrap Dashboard - img: /assets/img/integrations/light-bootstrap-dashboard.png - plat: WebPlatform - url: 'https://www.creative-tim.com/product/light-bootstrap-dashboard' - - name: Radix Theme - img: /assets/img/integrations/radix-theme-drupal.png - plat: Drupal - url: 'https://www.drupal.org/project/radix' - - name: Bootstrap 3 Sublime Plugin - img: /assets/img/integrations/bs3-sublime-plugin.png - plat: Sublime - url: 'https://github.com/JasonMortonNZ/bs3-sublime-plugin' - - name: Bootstrap 3 Jade Sublime Plugin - img: /assets/img/integrations/bs3-jade-sublime-plugin.png - plat: Sublime - url: 'https://github.com/rs459/bootstrap3-jade-sublime-plugin' - - name: Bootstrap Editor and Builder - img: /assets/img/integrations/bootply.png - plat: Bootply - url: 'https://www.bootply.com/' - - name: React-Bootstrap - img: /assets/img/integrations/react-bootstrap.png - plat: React - url: 'https://react-bootstrap.github.io/' -friends: - - name: ScaleScale - url: 'https://www.scalescale.com/' - - name: Bootstrap - url: 'https://getbootstrap.com/' - - name: Sticker Mule - url: 'https://www.stickermule.com/' - - name: nixCraft - url: 'https://www.cyberciti.biz/' - - name: Fort Awesome - url: 'https://fortawesome.com/' diff --git a/config/index.js b/config/index.js new file mode 100644 index 000000000..f28cb3b5f --- /dev/null +++ b/config/index.js @@ -0,0 +1,24 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const yaml = require('js-yaml'); +const csp = require('./helmet-csp.js'); + +function loadConfig(file) { + return yaml.safeLoad(fs.readFileSync(path.join(__dirname, file)), 'utf8'); +} + +function getConfigPath(file) { + return path.join(__dirname, file); +} + +['_app.yml', '_extras.yml', '_files.yml'].forEach((config) => { + module.exports = Object.assign(loadConfig(config), module.exports); +}); + +module.exports.CSP = csp; +module.exports.getConfigPath = getConfigPath; +module.exports.loadConfig = loadConfig; + +// vim: ft=javascript sw=4 sts=4 et: diff --git a/lib/helpers.js b/lib/helpers.js index 0cd93aa96..c0406b12c 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,53 +1,42 @@ 'use strict'; const fs = require('fs'); -const path = require('path'); const semver = require('semver'); const sri = require('sri-toolbox'); -const yaml = require('js-yaml'); -const CONFIG_FILE_PATH = path.resolve(__dirname, '../config/_config.yml'); +const config = require('../config'); function sriDigest(file, fromString) { file = fromString ? file : fs.readFileSync(file); return sri.generate({ algorithms: ['sha384'] }, file); } -function selectedTheme(config, selected) { - const themes = config.bootswatch4.themes; +function selectedTheme(cfg, selected) { + const themes = cfg.bootswatch4.themes; if (typeof selected === 'undefined' || selected >= themes.length) { - return config.theme; + return cfg.theme; } return parseInt(selected, 10) === 0 || parseInt(selected, 10) ? parseInt(selected, 10) : - config.theme; + cfg.theme; } -function getTheme(config, selected) { - const themes = config.bootswatch4.themes; +function getTheme(cfg, selected) { + const themes = cfg.bootswatch4.themes; - selected = selectedTheme(config, selected); + selected = selectedTheme(cfg, selected); return { - uri: config.bootswatch4.bootstrap - .replace('SWATCH_VERSION', config.bootswatch4.version) + uri: cfg.bootswatch4.bootstrap + .replace('SWATCH_VERSION', cfg.bootswatch4.version) .replace('SWATCH_NAME', themes[selected].name), sri: themes[selected].sri }; } -function getConfig() { - return yaml.safeLoad(fs.readFileSync(CONFIG_FILE_PATH), 'utf8'); -} - -function getConfigPath() { - return CONFIG_FILE_PATH; -} - function generateDataJson() { - const config = getConfig(); const data = { timestamp: new Date().toISOString(), bootstrap: {}, @@ -73,8 +62,6 @@ function generateDataJson() { } module.exports = { - getConfig, - getConfigPath, generateDataJson, theme: { selected: selectedTheme, diff --git a/routes/appendLocals.js b/routes/appendLocals.js index 9331cd789..fccaaf200 100644 --- a/routes/appendLocals.js +++ b/routes/appendLocals.js @@ -2,13 +2,13 @@ const path = require('path'); const helpers = require('../lib/helpers.js'); +const config = require('../config'); const digest = helpers.sri.digest; - const SRI_CACHE = {}; function appendLocals(req, res) { - const totalThemes = helpers.getConfig().bootswatch4.themes.length; + const totalThemes = config.bootswatch4.themes.length; const TITLE_SUFFIX = 'BootstrapCDN by StackPath'; let proto = req.get('x-forwarded-proto'); diff --git a/scripts/integrity.js b/scripts/integrity.js index cdd43c286..8a3938d01 100644 --- a/scripts/integrity.js +++ b/scripts/integrity.js @@ -5,11 +5,11 @@ const fs = require('fs'); const path = require('path'); const yaml = require('js-yaml'); -const helpers = require('../lib/helpers.js'); const sri = require('./sri.js'); +const config = require('../config'); -const configFile = helpers.getConfigPath(); -const config = helpers.getConfig(); +const filesConfig = config.loadConfig('_files.yml'); +const configFile = config.getConfigPath('_files.yml'); // create backup file fs.createReadStream(configFile) @@ -33,10 +33,10 @@ function exists(file) { // bootswatch{3,4} ((() => { ['bootswatch3', 'bootswatch4'].forEach((key) => { - const bootswatch = buildPath(config[key].bootstrap); + const bootswatch = buildPath(filesConfig[key].bootstrap); - for (const theme of config[key].themes) { - const file = bootswatch.replace('SWATCH_VERSION', config[key].version) + for (const theme of filesConfig[key].themes) { + const file = bootswatch.replace('SWATCH_VERSION', filesConfig[key].version) .replace('SWATCH_NAME', theme.name); if (exists(file)) { // always regenerate @@ -48,7 +48,7 @@ function exists(file) { // bootlint ((() => { - for (const bootlint of config.bootlint) { + for (const bootlint of filesConfig.bootlint) { const file = buildPath(bootlint.javascript); if (exists(file)) { // always regenerate @@ -59,7 +59,7 @@ function exists(file) { // bootstrap ((() => { - for (const bootstrap of config.bootstrap) { + for (const bootstrap of filesConfig.bootstrap) { // Skip when the key doesn't exist if (typeof bootstrap.javascriptBundle === 'undefined') { continue; @@ -85,7 +85,7 @@ function exists(file) { // fontawesome ((() => { - for (const fontawesome of config.fontawesome) { + for (const fontawesome of filesConfig.fontawesome) { const stylesheet = buildPath(fontawesome.stylesheet); if (exists(stylesheet)) { @@ -94,4 +94,4 @@ function exists(file) { } }))(); -fs.writeFileSync(configFile, yaml.dump(config, { lineWidth: -1 })); +fs.writeFileSync(configFile, yaml.dump(filesConfig, { lineWidth: -1 })); diff --git a/scripts/update_bootswatch.js b/scripts/update_bootswatch.js index aee93461c..e470ffa08 100755 --- a/scripts/update_bootswatch.js +++ b/scripts/update_bootswatch.js @@ -5,7 +5,7 @@ const fs = require('fs'); const path = require('path'); const request = require('request'); -const helpers = require('../lib/helpers.js'); +const config = require('../config'); const version = process.argv[2]; const verMajor = version[0]; @@ -16,7 +16,6 @@ if (!version) { } const bootswatchDir = path.join(__dirname, '../public/bootswatch', version); -const config = helpers.getConfig(); const files = [ 'https://bootswatch.com/%d/%s/bootstrap.min.css', diff --git a/test/test_helpers.js b/test/test_helpers.js index 21cd502f5..fcddeb5d3 100644 --- a/test/test_helpers.js +++ b/test/test_helpers.js @@ -9,10 +9,9 @@ const htmlEncode = require('htmlencode').htmlEncode; const mockDate = require('mockdate'); const request = require('request'); const validator = require('html-validator'); -const app = require('../app.js'); -const helpers = require('../lib/helpers.js'); -const config = helpers.getConfig(); +const app = require('../app.js'); +const config = require('../config'); // The server object holds the server instance across all tests; // We start it in the first test and close it in the last one,