diff --git a/README.md b/README.md index c56a4fe6f6..0522de051a 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,36 @@ Once logged in, visit [https://127.0.0.1:8080/editor](https://127.0.0.1:8080/edi `yarn test` or `yarn test:ci` +### Deployment and Setup + +For deployment, there are several places you can set configuration options. Obojobo uses a hybrid of json files and environment variables. We are moving more toward using more environment variables. However, json config files can be found in several obojobo packages (they are usually in server/config/*.json files). Below are a set of environment variables that we recommend using + +- `CDN_ASSET_HOST`: If you want to host compiled browser assets (js/css/etc) on a CDN. Ex value: `https://site.com` +- `NODE_ENV`: determines which environment from the config files will be used. Use production unless you have a use for multiple environments. +- `DB_USER`: db user +- `DB_PASS`: db pass +- `DB_HOST`: db host +- `DB_NAME`: db name +- `DB_PORT`: db port +- `DB_SSL_JSON`: enable or disable using ssl to connect to the database +- `DB_TIMEOUT`: ex: 29000. Used to set the pg-promise settings for query_timeout and statement_timeout. +- `OBO_LTI_KEYS_JSON`: ex: '{"key1":"secret1","key2":"secret2}'. Use json string to define valid lti key/secret pairs. +- `OBO_LTI_USERNAME_PARAM`: What lti launch param do you want to use as the username in obojobo. ex: `user_id` or `lis_person_sourcedid` +- `OBO_COOKIE_SECRET`: Randomized secret key that is used to encrypt cookies. See the docs for express-session. +- `OBO_COOKIE_SECURE`: Set to true when using https. See docs for express-session. Used to set secure, sameSite and httpOnly options. +- `DEBUG`: Logging Verbosity. Use obojobo_server:* for more output. ex: `obojobo_server:error,obojobo_server:warn` +- `YARN_PRODUCTION`: Set to true. Makes sure yarn install includes dev dependencies. +- `OBO_DEMO_PURGE_MODE`: Automatically purge old data for demo purposes or to limit database growth. Additional addon setup required. ex: disabled (default), DANGER-delete-HISTORY-data, DANGER-delete-ALL-data +- `OBO_DEMO_PURGE_DAYS_AGO`: If purge mode is enabled, purge data older than this many days. ex: 7 +- `OBO_EDITLOCK_TIME_UNTIL_RELEASE_MINUTES`: Editor lock: period of inactivity to release the lock. ex: 45 +- `OBO_EDITLOCK_TIME_UNTIL_WARN_MINUTES`: Editor lock: period of inactivity to warn the user about lock release. ex: 40 +- `OBO_EDITLOCK_DB_LOCK_DURATION`: Editor lock: period of time a lock lasts unless it is renewed. ex: 5 +- `OBO_LTI_GUID`: A GUID unique to this install of Obojobo. Google LTI launch param tool_consumer_instance_guid for more details. ex: `edu.your-school.obojobo` +- `MATERIA_OAUTH_KEY`: LTI Oauth key used just for the optional Materia integration. +- `MATERIA_OAUTH_SECRET`: LTI Oauth secret used the optional Materia integration. +- `MATERIA_HOST`: URL for the optional Materia integration. ex: https://your.materia.com +- `OBO_OPTIONAL_NODES`: Comma separated list of optional nodes that are already installed to enable. ex: `*` or `obojobo-chunks-materia` + ### Special Thanks Support for this work was provided by the National Science Foundation Scholarships in Science, Technology, Engineering, and Mathematics (S-STEM) program under [Award No.1643835](https://www.nsf.gov/awardsearch/showAward?AWD_ID=1643835). Any opinions, findings, conclusions and recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. diff --git a/packages/app/obojobo-express/__tests__/asset_resolver.test.js b/packages/app/obojobo-express/__tests__/asset_resolver.test.js index d6549bb5ae..289286c3b3 100644 --- a/packages/app/obojobo-express/__tests__/asset_resolver.test.js +++ b/packages/app/obojobo-express/__tests__/asset_resolver.test.js @@ -14,10 +14,12 @@ const { assetForEnv } = require('../server/asset_resolver') describe('Asset Resolver', () => { const originalNODE_ENV = process.env.NODE_ENV const originalIS_WEBPACK = process.env.IS_WEBPACK + const originalCDN_ASSET_HOST = process.env.CDN_ASSET_HOST afterAll(() => { process.env.NODE_ENV = originalNODE_ENV process.env.IS_WEBPACK = originalIS_WEBPACK + process.env.CDN_ASSET_HOST = originalCDN_ASSET_HOST }) beforeEach(() => { @@ -25,6 +27,7 @@ describe('Asset Resolver', () => { delete process.env.NODE_ENV delete process.env.ASSET_ENV delete process.env.IS_WEBPACK + delete process.env.CDN_ASSET_HOST }) test('assetForEnv builds pattern for dev server', () => { @@ -90,4 +93,14 @@ describe('Asset Resolver', () => { expect(webpackAssetPath('subdir/test.json')).toBe('/static-from-manifest/subdir/test.json') expect(webpackAssetPath('asset-that-doesnt-exist.js')).toBe(undefined) //eslint-disable-line no-undefined }) + + test('CDN_ASSET_HOST is prepended to manifest assets when present', () => { + process.env.IS_WEBPACK = 'false' + process.env.CDN_ASSET_HOST = 'https://test-cdn-value.com' + const { webpackAssetPath } = require('../server/asset_resolver') + + expect(webpackAssetPath('test.js')).toBe( + 'https://test-cdn-value.com/static-from-manifest/test.js' + ) + }) }) diff --git a/packages/app/obojobo-express/server/asset_resolver.js b/packages/app/obojobo-express/server/asset_resolver.js index a66bc04d32..be29fc15f5 100644 --- a/packages/app/obojobo-express/server/asset_resolver.js +++ b/packages/app/obojobo-express/server/asset_resolver.js @@ -43,12 +43,19 @@ const getEnv = forceEnvTo => { return (env || NODE_ENV).toLowerCase() } +const resolveFromManifest = assetName => { + // eslint-disable-next-line no-undefined + if (!manifest[assetName]) return undefined + return `${process.env.CDN_ASSET_HOST || ''}${manifest[assetName]}` +} + +// use the original name in the static path +const resolveFromWebpackDevServer = assetName => `/static/${assetName}` + const IS_WEBPACK = process.env.IS_WEBPACK === 'true' // NOTE: manifest created via `yarn build` const manifest = IS_WEBPACK ? {} : require('./public/compiled/manifest.json') -const webpackAssetPath = IS_WEBPACK - ? assetName => `/static/${assetName}` // use the original name in the static path - : assetName => manifest[assetName] // return path from the manifest +const webpackAssetPath = IS_WEBPACK ? resolveFromWebpackDevServer : resolveFromManifest module.exports = { assetForEnv, diff --git a/packages/app/obojobo-repository/server/events.js b/packages/app/obojobo-repository/server/events.js index 9eb7691c76..b9c11b8add 100644 --- a/packages/app/obojobo-repository/server/events.js +++ b/packages/app/obojobo-repository/server/events.js @@ -28,7 +28,10 @@ oboEvents.on('HTTP_NOT_AUTHORIZED', ({ req, res, next }) => { title: 'Not Authorized', children: 'You do not have the permissions required to view this page.', currentUser: req.currentUser, - appCSSUrl + appCSSUrl, + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-error.jsx', props) }) @@ -42,7 +45,10 @@ oboEvents.on('HTTP_NOT_FOUND', ({ req, res, next }) => { title: 'Not Found', children: "The page you requested doesn't exist.", currentUser: req.currentUser, - appCSSUrl + appCSSUrl, + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-error.jsx', props) }) @@ -56,7 +62,10 @@ oboEvents.on('HTTP_UNEXPECTED', ({ req, res, next }) => { title: 'Unexpected Server Error', children: 'There was an internal server error.', currentUser: req.currentUser, - appCSSUrl + appCSSUrl, + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-error.jsx', props) }) diff --git a/packages/app/obojobo-repository/server/routes/dashboard.js b/packages/app/obojobo-repository/server/routes/dashboard.js index 2f5e759744..3cfac192a7 100644 --- a/packages/app/obojobo-repository/server/routes/dashboard.js +++ b/packages/app/obojobo-repository/server/routes/dashboard.js @@ -30,7 +30,10 @@ router currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('dashboard.css'), - appJsUrl: webpackAssetPath('dashboard.js') + appJsUrl: webpackAssetPath('dashboard.js'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-dashboard-server.jsx', props) }) diff --git a/packages/app/obojobo-repository/server/routes/library.js b/packages/app/obojobo-repository/server/routes/library.js index 375aef64b8..08d1b748d6 100644 --- a/packages/app/obojobo-repository/server/routes/library.js +++ b/packages/app/obojobo-repository/server/routes/library.js @@ -11,6 +11,7 @@ const { requireDraftId, getCurrentUser } = require('obojobo-express/server/express_validators') +const { populateClientGlobals } = require('../../shared/react-utils') const publicLibCollectionId = require('../../shared/publicLibCollectionId') @@ -21,7 +22,10 @@ router const props = { currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! - appCSSUrl: webpackAssetPath('homepage.css') + appCSSUrl: webpackAssetPath('homepage.css'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-homepage.jsx', props) }) @@ -102,7 +106,10 @@ router const props = { currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! - appCSSUrl: webpackAssetPath('repository.css') + appCSSUrl: webpackAssetPath('repository.css'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-login.jsx', props) }) @@ -122,8 +129,12 @@ router pageCount: 1, currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! - appCSSUrl: webpackAssetPath('repository.css') + appCSSUrl: webpackAssetPath('repository.css'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } + populateClientGlobals(props) res.render('pages/page-library.jsx', props) }) .catch(res.unexpected) @@ -159,8 +170,12 @@ router // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('repository.css'), appJsUrl: webpackAssetPath('page-module.js'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + }, canCopy } + res.render('pages/page-module-server.jsx', props) } catch (e) { res.unexpected(e) diff --git a/packages/app/obojobo-repository/server/routes/library.test.js b/packages/app/obojobo-repository/server/routes/library.test.js index 757fd6afe9..049a3542bf 100644 --- a/packages/app/obojobo-repository/server/routes/library.test.js +++ b/packages/app/obojobo-repository/server/routes/library.test.js @@ -2,6 +2,7 @@ jest.mock('../models/collection') jest.mock('../models/draft_summary') jest.mock('obojobo-express/server/models/user') jest.mock('../models/draft_permissions') +jest.mock('../../shared/react-utils') jest.mock('trianglify') jest.unmock('fs') // need fs working for view rendering jest.unmock('express') // we'll use supertest + express for this @@ -17,14 +18,11 @@ jest.mock( jest.setTimeout(10000) // extend test timeout? const publicLibCollectionId = require('../../shared/publicLibCollectionId') - let trianglify - let Collection let DraftSummary let UserModel let DraftPermissions - // override requireCurrentUser for tests to provide our own user let mockCurrentUser diff --git a/packages/app/obojobo-repository/server/routes/stats.js b/packages/app/obojobo-repository/server/routes/stats.js index 766d8370ad..738e8594d5 100644 --- a/packages/app/obojobo-repository/server/routes/stats.js +++ b/packages/app/obojobo-repository/server/routes/stats.js @@ -18,7 +18,10 @@ router currentUser: req.currentUser, // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('stats.css'), - appJsUrl: webpackAssetPath('stats.js') + appJsUrl: webpackAssetPath('stats.js'), + globals: { + staticAssetUrl: process.env.CDN_ASSET_HOST || '' + } } res.render('pages/page-stats-server.jsx', props) }) diff --git a/packages/app/obojobo-repository/shared/components/module-image.jsx b/packages/app/obojobo-repository/shared/components/module-image.jsx index f58cc9061a..ee5f6f4f1c 100644 --- a/packages/app/obojobo-repository/shared/components/module-image.jsx +++ b/packages/app/obojobo-repository/shared/components/module-image.jsx @@ -1,10 +1,15 @@ require('./module-image.scss') const React = require('react') +const clientGlobals = require('../util/client-globals') const ModuleImage = props => (