diff --git a/layouts/errors/500.ejs b/layouts/errors/500.ejs
index 1716a957..91dbb175 100644
--- a/layouts/errors/500.ejs
+++ b/layouts/errors/500.ejs
@@ -8,6 +8,9 @@
<%- template('error.500.heading') %>
<%- template('error.500.message') %>
+ <% if (locals.isDev && locals.err && locals.err.message) { %>
+
Error: <%- locals.err.message %>
+ <% } %>
<%- include('partials/search', {style: 'homepage'}) %>
diff --git a/server/errors/index.js b/server/errors/index.js
new file mode 100644
index 00000000..da338158
--- /dev/null
+++ b/server/errors/index.js
@@ -0,0 +1,22 @@
+const messages = require('./messages')
+
+// For now, this should just throw for things that would stop the app from booting.
+
+module.exports = () => {
+ const { errorMessages } = messages
+ const { APPROVED_DOMAINS, DRIVE_TYPE, DRIVE_ID } = process.env
+ const errors = []
+
+ if (!APPROVED_DOMAINS) errors.push(errorMessages.noApprovedDomains)
+ if (!DRIVE_TYPE) errors.push(errorMessages.noDriveType)
+ if (!DRIVE_ID) errors.push(errorMessages.noDriveID)
+
+ if (errors.length) {
+ console.log('***********************************************')
+ console.log('Your library instance has configuration issues:')
+ errors.forEach((message) => console.error(` > ${message}`))
+ console.log('Address these issues and restart the app.')
+ console.log('***********************************************')
+ process.exit(1)
+ }
+}
diff --git a/server/errors/messages.json b/server/errors/messages.json
new file mode 100644
index 00000000..b639249c
--- /dev/null
+++ b/server/errors/messages.json
@@ -0,0 +1,8 @@
+{
+ "errorMessages": {
+ "noApprovedDomains": "You must set the APPROVED_DOMAINS environment variable to a list of domains or a regular expression.",
+ "noDriveType": "No DRIVE_TYPE env variable set. Please set it to 'team' or 'folder.'",
+ "noDriveID": "No DRIVE_ID env variable set. Set this environment variable to the ID of your drive or folder and restart the app.",
+ "noFilesFound": "No files found. Ensure your DRIVE_ID and DRIVE_TYPE environment variables are set correctly and that your service account's email is shared with your drive or folder.\n\nIf you just added the account, wait a minute and try again."
+ }
+}
diff --git a/server/index.js b/server/index.js
index 1fac976a..372ab253 100644
--- a/server/index.js
+++ b/server/index.js
@@ -7,6 +7,7 @@ const csp = require('helmet-csp')
const {middleware: cache} = require('./cache')
const {getMeta} = require('./list')
const {allMiddleware, requireWithFallback} = require('./utils')
+const checkErrors = require('./errors')
const userInfo = require('./routes/userInfo')
const pages = require('./routes/pages')
const categories = require('./routes/categories')
@@ -15,6 +16,8 @@ const readingHistory = require('./routes/readingHistory')
const redirects = require('./routes/redirects')
const errorPages = require('./routes/errors')
+checkErrors()
+
const userAuth = requireWithFallback('userAuth')
const customCsp = requireWithFallback('csp')
diff --git a/server/routes/errors.js b/server/routes/errors.js
index 5e85a9de..1aac0e58 100644
--- a/server/routes/errors.js
+++ b/server/routes/errors.js
@@ -63,12 +63,14 @@ module.exports = async (err, req, res, next) => {
const code = messages[err.message] || 500
log.error(`Serving an error page for ${req.url}`, err)
const inlined = await loadInlineAssets()
+
res.status(code)
res.format({
html: () => {
res.render(`errors/${code}`, {
inlineCSS: inlined.css,
+ isDev: process.env.NODE_ENV === 'development',
err,
template: inlined.stringTemplate
})
diff --git a/server/routes/pages.js b/server/routes/pages.js
index 9acd77f2..6bd6b556 100644
--- a/server/routes/pages.js
+++ b/server/routes/pages.js
@@ -2,6 +2,9 @@
const search = require('../search')
+const {getAuth} = require('../auth')
+const {errorMessages} = require('../errors/messages.json')
+
const router = require('express-promise-router')()
const {getTree, getFilenames, getMeta, getTagged} = require('../list')
@@ -62,6 +65,12 @@ async function handlePage(req, res) {
if (page === 'categories' || page === 'index') {
const tree = await getTree()
+ if (!tree.children) {
+ // pull the auth client email to make debugging easier:
+ const authClient = await getAuth()
+ const errMsg = errorMessages.noFilesFound.replace('email', `email (${authClient.email}
)`)
+ throw new Error(errMsg)
+ }
const categories = buildDisplayCategories(tree)
res.format({
html: () => {
diff --git a/server/userAuth.js b/server/userAuth.js
index cacd5a91..fcca4593 100644
--- a/server/userAuth.js
+++ b/server/userAuth.js
@@ -12,6 +12,8 @@ const {stringTemplate: template} = require('./utils')
const router = require('express-promise-router')()
const domains = new Set(process.env.APPROVED_DOMAINS.split(/,\s?/g))
+const isDev = process.env.NODE_ENV === 'development'
+
const authStrategies = ['google', 'Slack']
let authStrategy = process.env.OAUTH_STRATEGY
@@ -38,8 +40,9 @@ if (isSlackOauth) {
} else {
// default to google auth
passport.use(new GoogleStrategy.Strategy({
- clientID: process.env.GOOGLE_CLIENT_ID,
- clientSecret: process.env.GOOGLE_CLIENT_SECRET,
+ // some value must be passed to passport, but in dev this value does not matter
+ clientID: isDev ? ' ' : process.env.GOOGLE_CLIENT_ID,
+ clientSecret: isDev ? ' ' : process.env.GOOGLE_CLIENT_SECRET,
callbackURL,
userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo',
passReqToCallback: true
@@ -82,7 +85,6 @@ router.get('/auth/redirect', passport.authenticate(authStrategy, {failureRedirec
})
router.use((req, res, next) => {
- const isDev = process.env.NODE_ENV === 'development'
const passportUser = (req.session.passport || {}).user || {}
if (isDev || (req.isAuthenticated() && isAuthorized(passportUser))) {
setUserInfo(req)
@@ -111,7 +113,7 @@ function isAuthorized(user) {
}
function setUserInfo(req) {
- if (process.env.NODE_ENV === 'development') {
+ if (isDev) { // userInfo shim for development
req.userInfo = {
email: process.env.TEST_EMAIL || template('footer.defaultEmail'),
userId: '10',