diff --git a/.eslintrc b/.eslintrc
index a4d6dcb0e..49166483b 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,5 +1,9 @@
{
- "extends": "airbnb",
+ "extends": [
+ "airbnb",
+ "prettier",
+ "prettier/react"
+ ],
"parser": "babel-eslint",
"rules": {
"arrow-body-style":[0, "as-needed"], // fix `this` shortcut on ES6 classes
diff --git a/frontend/components/Switch.js b/frontend/components/Switch.js
index c897940dd..1c983e400 100644
--- a/frontend/components/Switch.js
+++ b/frontend/components/Switch.js
@@ -8,10 +8,7 @@ import { actionColor } from 'styles/constants.scss';
* Binary toggle switch component
*/
-const Switch = observer(({
- checked,
- ...props
-}) => {
+const Switch = observer(({ checked, ...props }) => {
const scale = '18';
const colors = {
success: actionColor,
@@ -19,9 +16,8 @@ const Switch = observer(({
};
const borderColor = actionColor;
-
- const color = checked ? colors.success : borderColor
- const transform = checked ? `translateX(${scale * 0.5}px)` : 'translateX(0)'
+ const color = checked ? colors.success : borderColor;
+ const transform = checked ? `translateX(${scale * 0.5}px)` : 'translateX(0)';
const sx = {
root: {
@@ -32,7 +28,7 @@ const Switch = observer(({
backgroundColor: checked ? 'currentcolor' : null,
borderRadius: 99999,
boxShadow: 'inset 0 0 0 2px',
- cursor: 'pointer'
+ cursor: 'pointer',
},
dot: {
width: scale,
@@ -44,29 +40,30 @@ const Switch = observer(({
boxShadow: 'inset 0 0 0 2px',
borderRadius: 99999,
color,
- backgroundColor: colors.white
- }
- }
+ backgroundColor: colors.white,
+ },
+ };
return (
${highlighted}`;
};
renderer.heading = (text, level) => {
@@ -25,10 +27,10 @@ renderer.heading = (text, level) => {
`;
};
-const convertToMarkdown = (text) => {
+const convertToMarkdown = text => {
// Add TOC
text = toc.insert(text || '', {
- slugify: (heading) => {
+ slugify: heading => {
// FIXME: E.g. `&` gets messed up
const headingSlug = _.escape(slug(heading));
return headingSlug;
@@ -46,6 +48,4 @@ const convertToMarkdown = (text) => {
});
};
-export {
- convertToMarkdown,
-};
+export { convertToMarkdown };
diff --git a/frontend/utils/random.js b/frontend/utils/random.js
index 50c05eaf4..5604e9071 100644
--- a/frontend/utils/random.js
+++ b/frontend/utils/random.js
@@ -1,7 +1,5 @@
const randomInteger = (min, max) => {
- return Math.floor(Math.random()*(max-min+1)+min);
-}
+ return Math.floor(Math.random() * (max - min + 1) + min);
+};
-export {
- randomInteger
-};
\ No newline at end of file
+export { randomInteger };
diff --git a/frontend/utils/testUtils.js b/frontend/utils/testUtils.js
index 9eae17b43..8570c9d4a 100644
--- a/frontend/utils/testUtils.js
+++ b/frontend/utils/testUtils.js
@@ -1,10 +1,8 @@
import renderer from 'react-test-renderer';
-const snap = (children) => {
+const snap = children => {
const component = renderer.create(children);
expect(component).toMatchSnapshot();
};
-export {
- snap,
-};
+export { snap };
diff --git a/package.json b/package.json
index 6584ea403..1514255eb 100644
--- a/package.json
+++ b/package.json
@@ -10,12 +10,20 @@
"start": "node index.js",
"dev": "cross-env NODE_ENV=development DEBUG=sql,cache,presenters ./node_modules/.bin/nodemon --watch server index.js",
"lint": "eslint frontend",
+ "prettier": "prettier --single-quote --trailing-comma es5 --write frontend/**/*.js server/**/*.js",
"deploy": "git push heroku master",
"heroku-postbuild": "npm run build && npm run sequelize db:migrate",
"sequelize": "./node_modules/.bin/sequelize",
"test": "npm run test:frontend && npm run test:server",
"test:frontend": "jest",
- "test:server": "jest --config=server/.jest-config --runInBand"
+ "test:server": "jest --config=server/.jest-config --runInBand",
+ "precommit": "lint-staged"
+ },
+ "lint-staged": {
+ "*.js": [
+ "yarn prettier",
+ "git add"
+ ]
},
"jest": {
"verbose": false,
@@ -150,14 +158,18 @@
"devDependencies": {
"babel-jest": "^15.0.0",
"enzyme": "^2.4.1",
+ "eslint-config-prettier": "^1.7.0",
+ "eslint-plugin-prettier": "^2.0.1",
"fetch-test-server": "^1.1.0",
"identity-obj-proxy": "^3.0.0",
"ignore-loader": "0.1.1",
"jest-cli": "^15.1.1",
"koa-webpack-dev-middleware": "1.2.0",
"koa-webpack-hot-middleware": "1.0.3",
+ "lint-staged": "^3.4.0",
"node-dev": "3.1.0",
"nodemon": "1.9.1",
+ "prettier": "^1.2.2",
"react-addons-test-utils": "^15.3.1",
"react-test-renderer": "^15.3.1"
}
diff --git a/server/api/apiKeys.js b/server/api/apiKeys.js
index 3570334e3..0a60efa89 100644
--- a/server/api/apiKeys.js
+++ b/server/api/apiKeys.js
@@ -8,10 +8,8 @@ import { ApiKey } from '../models';
const router = new Router();
-router.post('apiKeys.create', auth(), async (ctx) => {
- const {
- name,
- } = ctx.body;
+router.post('apiKeys.create', auth(), async ctx => {
+ const { name } = ctx.body;
ctx.assertPresent(name, 'name is required');
const user = ctx.state.user;
@@ -26,15 +24,13 @@ router.post('apiKeys.create', auth(), async (ctx) => {
};
});
-router.post('apiKeys.list', auth(), pagination(), async (ctx) => {
+router.post('apiKeys.list', auth(), pagination(), async ctx => {
const user = ctx.state.user;
const keys = await ApiKey.findAll({
where: {
userId: user.id,
},
- order: [
- ['createdAt', 'DESC'],
- ],
+ order: [['createdAt', 'DESC']],
offset: ctx.state.pagination.offset,
limit: ctx.state.pagination.limit,
});
@@ -49,10 +45,8 @@ router.post('apiKeys.list', auth(), pagination(), async (ctx) => {
};
});
-router.post('apiKeys.delete', auth(), async (ctx) => {
- const {
- id,
- } = ctx.body;
+router.post('apiKeys.delete', auth(), async ctx => {
+ const { id } = ctx.body;
ctx.assertPresent(id, 'id is required');
const user = ctx.state.user;
diff --git a/server/api/auth.js b/server/api/auth.js
index ec666231d..e2069627c 100644
--- a/server/api/auth.js
+++ b/server/api/auth.js
@@ -9,7 +9,7 @@ import { User, Team } from '../models';
const router = new Router();
-router.post('auth.signup', async (ctx) => {
+router.post('auth.signup', async ctx => {
const { username, name, email, password } = ctx.request.body;
ctx.assertPresent(username, 'name is required');
@@ -19,11 +19,19 @@ router.post('auth.signup', async (ctx) => {
ctx.assertPresent(password, 'password is required');
if (await User.findOne({ where: { email } })) {
- throw apiError(400, 'user_exists_with_email', 'User already exists with this email');
+ throw apiError(
+ 400,
+ 'user_exists_with_email',
+ 'User already exists with this email'
+ );
}
if (await User.findOne({ where: { username } })) {
- throw apiError(400, 'user_exists_with_username', 'User already exists with this username');
+ throw apiError(
+ 400,
+ 'user_exists_with_username',
+ 'User already exists with this username'
+ );
}
const user = await User.create({
@@ -33,13 +41,15 @@ router.post('auth.signup', async (ctx) => {
password,
});
- ctx.body = { data: {
- user: await presentUser(ctx, user),
- accessToken: user.getJwtToken(),
- } };
+ ctx.body = {
+ data: {
+ user: await presentUser(ctx, user),
+ accessToken: user.getJwtToken(),
+ },
+ };
});
-router.post('auth.login', async (ctx) => {
+router.post('auth.login', async ctx => {
const { username, password } = ctx.request.body;
ctx.assertPresent(username, 'username/email is required');
@@ -47,10 +57,9 @@ router.post('auth.login', async (ctx) => {
let user;
if (username) {
- user = await User.findOne({ where: Sequelize.or(
- { email: username },
- { username },
- ) });
+ user = await User.findOne({
+ where: Sequelize.or({ email: username }, { username }),
+ });
} else {
throw apiError(400, 'invalid_credentials', 'username or email is invalid');
}
@@ -67,13 +76,15 @@ router.post('auth.login', async (ctx) => {
throw apiError(400, 'invalid_password', 'Invalid password');
}
- ctx.body = { data: {
- user: await presentUser(ctx, user),
- accessToken: user.getJwtToken(),
- } };
+ ctx.body = {
+ data: {
+ user: await presentUser(ctx, user),
+ accessToken: user.getJwtToken(),
+ },
+ };
});
-router.post('auth.slack', async (ctx) => {
+router.post('auth.slack', async ctx => {
const { code } = ctx.body;
ctx.assertPresent(code, 'code is required');
@@ -86,7 +97,9 @@ router.post('auth.slack', async (ctx) => {
let data;
try {
- const response = await fetch(`https://slack.com/api/oauth.access?${querystring.stringify(body)}`);
+ const response = await fetch(
+ `https://slack.com/api/oauth.access?${querystring.stringify(body)}`
+ );
data = await response.json();
} catch (e) {
throw httpErrors.BadRequest();
@@ -97,7 +110,11 @@ router.post('auth.slack', async (ctx) => {
// Temp to block
const allowedSlackDomains = process.env.ALLOWED_SLACK_DOMAINS.split(',');
if (!allowedSlackDomains.includes(data.team.domain)) {
- throw apiError(400, 'invalid_slack_team', 'Atlas is currently in private beta');
+ throw apiError(
+ 400,
+ 'invalid_slack_team',
+ 'Atlas is currently in private beta'
+ );
}
// User
@@ -138,14 +155,16 @@ router.post('auth.slack', async (ctx) => {
await team.createFirstAtlas(user.id);
}
- ctx.body = { data: {
- user: await presentUser(ctx, user),
- team: await presentTeam(ctx, team),
- accessToken: user.getJwtToken(),
- } };
+ ctx.body = {
+ data: {
+ user: await presentUser(ctx, user),
+ team: await presentTeam(ctx, team),
+ accessToken: user.getJwtToken(),
+ },
+ };
});
-router.post('auth.slackCommands', async (ctx) => {
+router.post('auth.slackCommands', async ctx => {
const { code } = ctx.body;
ctx.assertPresent(code, 'code is required');
@@ -158,7 +177,9 @@ router.post('auth.slackCommands', async (ctx) => {
let data;
try {
- const response = await fetch(`https://slack.com/api/oauth.access?${querystring.stringify(body)}`);
+ const response = await fetch(
+ `https://slack.com/api/oauth.access?${querystring.stringify(body)}`
+ );
data = await response.json();
} catch (e) {
throw httpErrors.BadRequest();
@@ -167,5 +188,4 @@ router.post('auth.slackCommands', async (ctx) => {
if (!data.ok) throw httpErrors.BadRequest(data.error);
});
-
export default router;
diff --git a/server/api/auth.test.js b/server/api/auth.test.js
index 68c43f00f..4ab3ddf47 100644
--- a/server/api/auth.test.js
+++ b/server/api/auth.test.js
@@ -37,7 +37,6 @@ describe('#auth.signup', async () => {
expect(body).toMatchSnapshot();
});
-
it('should require valid email', async () => {
const res = await server.post('/api/auth.signup', {
body: {
diff --git a/server/api/collections.js b/server/api/collections.js
index 46b5824b7..86650a9e4 100644
--- a/server/api/collections.js
+++ b/server/api/collections.js
@@ -9,12 +9,8 @@ import { Atlas } from '../models';
const router = new Router();
-router.post('collections.create', auth(), async (ctx) => {
- const {
- name,
- description,
- type,
- } = ctx.body;
+router.post('collections.create', auth(), async ctx => {
+ const { name, description, type } = ctx.body;
ctx.assertPresent(name, 'name is required');
const user = ctx.state.user;
@@ -32,7 +28,7 @@ router.post('collections.create', auth(), async (ctx) => {
};
});
-router.post('collections.info', auth(), async (ctx) => {
+router.post('collections.info', auth(), async ctx => {
const { id } = ctx.body;
ctx.assertPresent(id, 'id is required');
@@ -51,25 +47,24 @@ router.post('collections.info', auth(), async (ctx) => {
};
});
-
-router.post('collections.list', auth(), pagination(), async (ctx) => {
+router.post('collections.list', auth(), pagination(), async ctx => {
const user = ctx.state.user;
const collections = await Atlas.findAll({
where: {
teamId: user.teamId,
},
- order: [
- ['updatedAt', 'DESC'],
- ],
+ order: [['updatedAt', 'DESC']],
offset: ctx.state.pagination.offset,
limit: ctx.state.pagination.limit,
});
// Atlases
let data = [];
- await Promise.all(collections.map(async (atlas) => {
- return data.push(await presentCollection(ctx, atlas, true));
- }));
+ await Promise.all(
+ collections.map(async atlas => {
+ return data.push(await presentCollection(ctx, atlas, true));
+ })
+ );
data = _.orderBy(data, ['updatedAt'], ['desc']);
@@ -79,7 +74,7 @@ router.post('collections.list', auth(), pagination(), async (ctx) => {
};
});
-router.post('collections.updateNavigationTree', auth(), async (ctx) => {
+router.post('collections.updateNavigationTree', auth(), async ctx => {
const { id, tree } = ctx.body;
ctx.assertPresent(id, 'id is required');
diff --git a/server/api/documents.js b/server/api/documents.js
index 4ffc018a6..537997cb9 100644
--- a/server/api/documents.js
+++ b/server/api/documents.js
@@ -12,7 +12,7 @@ import { Document, Atlas } from '../models';
const router = new Router();
-const getDocumentForId = async (id) => {
+const getDocumentForId = async id => {
try {
let document;
if (isUUID(id)) {
@@ -38,7 +38,7 @@ const getDocumentForId = async (id) => {
};
// FIXME: This really needs specs :/
-router.post('documents.info', auth(), async (ctx) => {
+router.post('documents.info', auth(), async ctx => {
const { id } = ctx.body;
ctx.assertPresent(id, 'id is required');
const document = await getDocumentForId(id);
@@ -69,7 +69,7 @@ router.post('documents.info', auth(), async (ctx) => {
}
});
-router.post('documents.search', auth(), async (ctx) => {
+router.post('documents.search', auth(), async ctx => {
const { query } = ctx.body;
ctx.assertPresent(query, 'query is required');
@@ -78,12 +78,16 @@ router.post('documents.search', auth(), async (ctx) => {
const documents = await Document.searchForUser(user, query);
const data = [];
- await Promise.all(documents.map(async (document) => {
- data.push(await presentDocument(ctx, document, {
- includeCollection: true,
- includeCollaborators: true,
- }));
- }));
+ await Promise.all(
+ documents.map(async document => {
+ data.push(
+ await presentDocument(ctx, document, {
+ includeCollection: true,
+ includeCollaborators: true,
+ })
+ );
+ })
+ );
ctx.body = {
pagination: ctx.state.pagination,
@@ -91,14 +95,8 @@ router.post('documents.search', auth(), async (ctx) => {
};
});
-
-router.post('documents.create', auth(), async (ctx) => {
- const {
- collection,
- title,
- text,
- parentDocument,
- } = ctx.body;
+router.post('documents.create', auth(), async ctx => {
+ const { collection, title, text, parentDocument } = ctx.body;
ctx.assertPresent(collection, 'collection is required');
ctx.assertPresent(title, 'title is required');
ctx.assertPresent(text, 'text is required');
@@ -115,7 +113,7 @@ router.post('documents.create', auth(), async (ctx) => {
const document = await (() => {
return new Promise(resolve => {
- lock(ownerCollection.id, 10000, async (done) => {
+ lock(ownerCollection.id, 10000, async done => {
// FIXME: should we validate the existance of parentDocument?
let parentDocumentObj = {};
if (parentDocument && ownerCollection.type === 'atlas') {
@@ -158,12 +156,8 @@ router.post('documents.create', auth(), async (ctx) => {
};
});
-router.post('documents.update', auth(), async (ctx) => {
- const {
- id,
- title,
- text,
- } = ctx.body;
+router.post('documents.update', auth(), async ctx => {
+ const { id, title, text } = ctx.body;
ctx.assertPresent(id, 'id is required');
ctx.assertPresent(title, 'title is required');
ctx.assertPresent(text, 'text is required');
@@ -171,7 +165,8 @@ router.post('documents.update', auth(), async (ctx) => {
const user = ctx.state.user;
const document = await getDocumentForId(id);
- if (!document || document.teamId !== user.teamId) throw httpErrors.BadRequest();
+ if (!document || document.teamId !== user.teamId)
+ throw httpErrors.BadRequest();
// Update document
document.title = title;
@@ -194,23 +189,22 @@ router.post('documents.update', auth(), async (ctx) => {
};
});
-router.post('documents.delete', auth(), async (ctx) => {
- const {
- id,
- } = ctx.body;
+router.post('documents.delete', auth(), async ctx => {
+ const { id } = ctx.body;
ctx.assertPresent(id, 'id is required');
const user = ctx.state.user;
const document = await getDocumentForId(id);
const collection = await Atlas.findById(document.atlasId);
- if (!document || document.teamId !== user.teamId) throw httpErrors.BadRequest();
+ if (!document || document.teamId !== user.teamId)
+ throw httpErrors.BadRequest();
// TODO: Add locking
if (collection.type === 'atlas') {
// Don't allow deletion of root docs
if (!document.parentDocumentId) {
- throw httpErrors.BadRequest('Unable to delete atlas\'s root document');
+ throw httpErrors.BadRequest("Unable to delete atlas's root document");
}
// Delete all chilren
diff --git a/server/api/hooks.js b/server/api/hooks.js
index c49503829..65b29b820 100644
--- a/server/api/hooks.js
+++ b/server/api/hooks.js
@@ -4,17 +4,14 @@ import { Document, User } from '../models';
const router = new Router();
-router.post('hooks.slack', async (ctx) => {
- const {
- token,
- user_id,
- text,
- } = ctx.body;
+router.post('hooks.slack', async ctx => {
+ const { token, user_id, text } = ctx.body;
ctx.assertPresent(token, 'token is required');
ctx.assertPresent(user_id, 'user_id is required');
ctx.assertPresent(text, 'text is required');
- if (token !== process.env.SLACK_VERIFICATION_TOKEN) throw httpErrors.BadRequest('Invalid token');
+ if (token !== process.env.SLACK_VERIFICATION_TOKEN)
+ throw httpErrors.BadRequest('Invalid token');
const user = await User.find({
where: {
@@ -31,7 +28,9 @@ router.post('hooks.slack', async (ctx) => {
const results = [];
let number = 1;
for (const document of documents) {
- results.push(`${number}. <${process.env.URL}${document.getUrl()}|${document.title}>`);
+ results.push(
+ `${number}. <${process.env.URL}${document.getUrl()}|${document.title}>`
+ );
number += 1;
}
diff --git a/server/api/user.js b/server/api/user.js
index 0a9fc1192..a68dcedcf 100644
--- a/server/api/user.js
+++ b/server/api/user.js
@@ -1,20 +1,17 @@
import uuid from 'uuid';
import Router from 'koa-router';
-import {
- makePolicy,
- signPolicy,
-} from '../utils/s3';
+import { makePolicy, signPolicy } from '../utils/s3';
import auth from './middlewares/authentication';
import { presentUser } from '../presenters';
const router = new Router();
-router.post('user.info', auth(), async (ctx) => {
+router.post('user.info', auth(), async ctx => {
ctx.body = { data: await presentUser(ctx, ctx.state.user) };
});
-router.post('user.s3Upload', auth(), async (ctx) => {
+router.post('user.s3Upload', auth(), async ctx => {
const { filename, kind, size } = ctx.body;
ctx.assertPresent(filename, 'filename is required');
ctx.assertPresent(kind, 'kind is required');
@@ -24,25 +21,27 @@ router.post('user.s3Upload', auth(), async (ctx) => {
const key = `${s3Key}/${filename}`;
const policy = makePolicy();
- ctx.body = { data: {
- maxUploadSize: process.env.AWS_S3_UPLOAD_MAX_SIZE,
- uploadUrl: process.env.AWS_S3_UPLOAD_BUCKET_URL,
- form: {
- AWSAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
- 'Cache-Control': 'max-age=31557600',
- 'Content-Type': kind,
- key,
- acl: 'public-read',
- signature: signPolicy(policy),
- policy,
+ ctx.body = {
+ data: {
+ maxUploadSize: process.env.AWS_S3_UPLOAD_MAX_SIZE,
+ uploadUrl: process.env.AWS_S3_UPLOAD_BUCKET_URL,
+ form: {
+ AWSAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
+ 'Cache-Control': 'max-age=31557600',
+ 'Content-Type': kind,
+ key,
+ acl: 'public-read',
+ signature: signPolicy(policy),
+ policy,
+ },
+ asset: {
+ contentType: kind,
+ url: `${process.env.AWS_S3_UPLOAD_BUCKET_URL}${s3Key}/${filename}`,
+ name: filename,
+ size,
+ },
},
- asset: {
- contentType: kind,
- url: `${process.env.AWS_S3_UPLOAD_BUCKET_URL}${s3Key}/${filename}`,
- name: filename,
- size,
- },
- } };
+ };
});
export default router;
diff --git a/server/middlewares/cache.js b/server/middlewares/cache.js
index 20e13cd9e..7a10ad642 100644
--- a/server/middlewares/cache.js
+++ b/server/middlewares/cache.js
@@ -8,7 +8,7 @@ export default function cache() {
ctx.cache.set = async (id, value) => {
ctx.cache[id] = value;
- }
+ };
ctx.cache.get = async (id, def) => {
if (ctx.cache[id]) {
diff --git a/server/middlewares/methodOverride.js b/server/middlewares/methodOverride.js
index ba5a39179..0c49e2a95 100644
--- a/server/middlewares/methodOverride.js
+++ b/server/middlewares/methodOverride.js
@@ -5,7 +5,7 @@ export default function methodOverride(_options) {
if (ctx.method === 'POST') {
ctx.body = ctx.request.body;
} else if (ctx.method === 'GET') {
- ctx.method= 'POST'; // eslint-disable-line
+ ctx.method = 'POST'; // eslint-disable-line
ctx.body = queryString.parse(ctx.querystring);
}
return next();
diff --git a/server/middlewares/subdomainRedirect.js b/server/middlewares/subdomainRedirect.js
index b297ccd08..8989bbbeb 100644
--- a/server/middlewares/subdomainRedirect.js
+++ b/server/middlewares/subdomainRedirect.js
@@ -2,9 +2,8 @@ export default function subdomainRedirect(options) {
return async function subdomainRedirectMiddleware(ctx, next) {
if (ctx.headers.host === 'beautifulatlas.com') {
ctx.redirect('https://www.' + ctx.headers.host + ctx.path);
- }
- else {
+ } else {
return next();
}
- }
-};
+ };
+}
diff --git a/server/migrations/20160619080644-initial.js b/server/migrations/20160619080644-initial.js
index a7f104a18..4a113bc12 100644
--- a/server/migrations/20160619080644-initial.js
+++ b/server/migrations/20160619080644-initial.js
@@ -1,12 +1,12 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
queryInterface.createTable('teams', {
id: {
type: 'UUID',
allowNull: false,
- primaryKey: true
+ primaryKey: true,
},
name: {
type: 'CHARACTER VARYING',
@@ -15,7 +15,7 @@ module.exports = {
slackId: {
type: 'CHARACTER VARYING',
allowNull: true,
- unique: true
+ unique: true,
},
slackData: {
type: 'JSONB',
@@ -28,14 +28,14 @@ module.exports = {
updatedAt: {
type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
- }
+ },
});
queryInterface.createTable('atlases', {
id: {
type: 'UUID',
allowNull: false,
- primaryKey: true
+ primaryKey: true,
},
name: {
type: 'CHARACTER VARYING',
@@ -68,14 +68,14 @@ module.exports = {
// model: "teams",
// key: "id",
// }
- }
+ },
});
queryInterface.createTable('users', {
id: {
type: 'UUID',
allowNull: false,
- primaryKey: true
+ primaryKey: true,
},
email: {
type: 'CHARACTER VARYING',
@@ -96,7 +96,8 @@ module.exports = {
},
slackAccessToken: {
type: 'bytea',
- allowNull: true, },
+ allowNull: true,
+ },
slackId: {
type: 'CHARACTER VARYING',
unique: true,
@@ -125,46 +126,48 @@ module.exports = {
// model: "teams",
// key: "id",
// }
- }
+ },
});
queryInterface.createTable('documents', {
- id:
- { type: 'UUID',
- allowNull: false,
- primaryKey: true },
- urlId:
- { type: 'CHARACTER VARYING',
- allowNull: false,
- unique: true, },
- private:
- { type: 'BOOLEAN',
- allowNull: false,
- defaultValue: true,
- },
- title:
- { type: 'CHARACTER VARYING',
- allowNull: false,
+ id: {
+ type: 'UUID',
+ allowNull: false,
+ primaryKey: true,
},
- text:
- { type: 'TEXT',
- allowNull: true,
+ urlId: {
+ type: 'CHARACTER VARYING',
+ allowNull: false,
+ unique: true,
},
- html:
- { type: 'TEXT',
- allowNull: true,
+ private: {
+ type: 'BOOLEAN',
+ allowNull: false,
+ defaultValue: true,
},
- preview:
- { type: 'TEXT',
- allowNull: true,
+ title: {
+ type: 'CHARACTER VARYING',
+ allowNull: false,
},
- createdAt:
- { type: 'TIMESTAMP WITH TIME ZONE',
- allowNull: false,
+ text: {
+ type: 'TEXT',
+ allowNull: true,
},
- updatedAt:
- { type: 'TIMESTAMP WITH TIME ZONE',
- allowNull: false,
+ html: {
+ type: 'TEXT',
+ allowNull: true,
+ },
+ preview: {
+ type: 'TEXT',
+ allowNull: true,
+ },
+ createdAt: {
+ type: 'TIMESTAMP WITH TIME ZONE',
+ allowNull: false,
+ },
+ updatedAt: {
+ type: 'TIMESTAMP WITH TIME ZONE',
+ allowNull: false,
},
userId: {
type: 'UUID',
@@ -189,11 +192,11 @@ module.exports = {
// model: "teams",
// key: "id",
// }
- }
+ },
});
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.dropAllTables();
- }
+ },
};
diff --git a/server/migrations/20160622043741-add-parent-document.js b/server/migrations/20160622043741-add-parent-document.js
index f8ecdbb7c..415e826ba 100644
--- a/server/migrations/20160622043741-add-parent-document.js
+++ b/server/migrations/20160622043741-add-parent-document.js
@@ -1,18 +1,14 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.addColumn(
- 'documents',
- 'parentDocumentId',
- {
- type: Sequelize.UUID,
- allowNull: true,
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.addColumn('documents', 'parentDocumentId', {
+ type: Sequelize.UUID,
+ allowNull: true,
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeColumn('documents', 'parentDocumentId');
- }
+ },
};
diff --git a/server/migrations/20160626063409-add-indexes.js b/server/migrations/20160626063409-add-indexes.js
index 6d886bd5c..a3878ff21 100644
--- a/server/migrations/20160626063409-add-indexes.js
+++ b/server/migrations/20160626063409-add-indexes.js
@@ -1,7 +1,7 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
queryInterface.addIndex('documents', ['urlId']);
queryInterface.addIndex('documents', ['id', 'atlasId']);
queryInterface.addIndex('documents', ['id', 'teamId']);
@@ -14,7 +14,7 @@ module.exports = {
queryInterface.addIndex('users', ['slackId']);
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeIndex('documents', ['urlId']);
queryInterface.removeIndex('documents', ['id', 'atlasId']);
queryInterface.removeIndex('documents', ['id', 'teamId']);
@@ -25,5 +25,5 @@ module.exports = {
queryInterface.removeIndex('teams', ['slackId']);
queryInterface.removeIndex('users', ['slackId']);
- }
+ },
};
diff --git a/server/migrations/20160626175224-add-revisions.js b/server/migrations/20160626175224-add-revisions.js
index 2e99cecd1..4a9f46c01 100644
--- a/server/migrations/20160626175224-add-revisions.js
+++ b/server/migrations/20160626175224-add-revisions.js
@@ -1,43 +1,43 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
queryInterface.createTable('revisions', {
id: {
type: 'UUID',
allowNull: false,
- primaryKey: true
+ primaryKey: true,
},
- title:
- { type: 'CHARACTER VARYING',
- allowNull: false,
+ title: {
+ type: 'CHARACTER VARYING',
+ allowNull: false,
},
- text:
- { type: 'TEXT',
- allowNull: true,
+ text: {
+ type: 'TEXT',
+ allowNull: true,
},
- html:
- { type: 'TEXT',
- allowNull: true,
+ html: {
+ type: 'TEXT',
+ allowNull: true,
},
- preview:
- { type: 'TEXT',
- allowNull: true,
+ preview: {
+ type: 'TEXT',
+ allowNull: true,
},
- createdAt:
- { type: 'TIMESTAMP WITH TIME ZONE',
- allowNull: false,
+ createdAt: {
+ type: 'TIMESTAMP WITH TIME ZONE',
+ allowNull: false,
},
- updatedAt:
- { type: 'TIMESTAMP WITH TIME ZONE',
- allowNull: false,
+ updatedAt: {
+ type: 'TIMESTAMP WITH TIME ZONE',
+ allowNull: false,
},
userId: {
type: 'UUID',
allowNull: false,
references: {
model: 'users',
- }
+ },
},
documentId: {
type: 'UUID',
@@ -45,36 +45,28 @@ module.exports = {
references: {
model: 'documents',
onDelete: 'CASCADE',
- }
+ },
},
});
- queryInterface.addColumn(
- 'documents',
- 'lastModifiedById',
- {
- type: 'UUID',
- allowNull: false,
- references: {
- model: 'users',
- }
- }
- );
+ queryInterface.addColumn('documents', 'lastModifiedById', {
+ type: 'UUID',
+ allowNull: false,
+ references: {
+ model: 'users',
+ },
+ });
- queryInterface.addColumn(
- 'documents',
- 'revisionCount',
- {
- type: 'INTEGER',
- defaultValue: 0
- }
- );
+ queryInterface.addColumn('documents', 'revisionCount', {
+ type: 'INTEGER',
+ defaultValue: 0,
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.dropTable('revisions');
queryInterface.removeColumn('documents', 'lastModifiedById');
queryInterface.removeColumn('documents', 'revisionCount');
- }
+ },
};
diff --git a/server/migrations/20160711071958-search-index.js b/server/migrations/20160711071958-search-index.js
index 5a73c0ec5..bb9c7ec06 100644
--- a/server/migrations/20160711071958-search-index.js
+++ b/server/migrations/20160711071958-search-index.js
@@ -1,7 +1,7 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
const searchDocument = `
ALTER TABLE documents ADD COLUMN "searchVector" tsvector;
CREATE INDEX documents_tsv_idx ON documents USING gin("searchVector");
@@ -40,7 +40,7 @@ ON atlases FOR EACH ROW EXECUTE PROCEDURE atlases_search_trigger();
queryInterface.sequelize.query(searchAtlas);
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
// TODO?
- }
+ },
};
diff --git a/server/migrations/20160726061511-atlas-creator.js b/server/migrations/20160726061511-atlas-creator.js
index 1d6285863..41ed9425c 100644
--- a/server/migrations/20160726061511-atlas-creator.js
+++ b/server/migrations/20160726061511-atlas-creator.js
@@ -1,18 +1,14 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.addColumn(
- 'atlases',
- 'creatorId',
- {
- type: Sequelize.UUID,
- allowNull: true,
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.addColumn('atlases', 'creatorId', {
+ type: Sequelize.UUID,
+ allowNull: true,
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeColumn('atlases', 'creatorId');
- }
+ },
};
diff --git a/server/migrations/20160812145029-document-atlas-soft-delete.js b/server/migrations/20160812145029-document-atlas-soft-delete.js
index a2081fc46..6f2864e91 100644
--- a/server/migrations/20160812145029-document-atlas-soft-delete.js
+++ b/server/migrations/20160812145029-document-atlas-soft-delete.js
@@ -1,28 +1,20 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.addColumn(
- 'atlases',
- 'deletedAt',
- {
- type: Sequelize.DATE,
- allowNull: true,
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.addColumn('atlases', 'deletedAt', {
+ type: Sequelize.DATE,
+ allowNull: true,
+ });
- queryInterface.addColumn(
- 'documents',
- 'deletedAt',
- {
- type: Sequelize.DATE,
- allowNull: true,
- }
- );
+ queryInterface.addColumn('documents', 'deletedAt', {
+ type: Sequelize.DATE,
+ allowNull: true,
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeColumn('atlases', 'deletedAt');
queryInterface.removeColumn('documents', 'deletedAt');
- }
+ },
};
diff --git a/server/migrations/20160814083127-paranoia-indeces.js b/server/migrations/20160814083127-paranoia-indeces.js
index b488d56f3..48853314d 100644
--- a/server/migrations/20160814083127-paranoia-indeces.js
+++ b/server/migrations/20160814083127-paranoia-indeces.js
@@ -1,7 +1,7 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
// Remove old indeces
queryInterface.removeIndex('documents', ['urlId']);
queryInterface.removeIndex('documents', ['id', 'atlasId']);
@@ -15,13 +15,17 @@ module.exports = {
queryInterface.addIndex('documents', ['urlId', 'deletedAt']);
queryInterface.addIndex('documents', ['id', 'atlasId', 'deletedAt']);
queryInterface.addIndex('documents', ['id', 'teamId', 'deletedAt']);
- queryInterface.addIndex('documents', ['parentDocumentId', 'atlasId', 'deletedAt']);
+ queryInterface.addIndex('documents', [
+ 'parentDocumentId',
+ 'atlasId',
+ 'deletedAt',
+ ]);
queryInterface.addIndex('atlases', ['id', 'deletedAt']);
queryInterface.addIndex('atlases', ['id', 'teamId', 'deletedAt']);
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.addIndex('documents', ['urlId']);
queryInterface.addIndex('documents', ['id', 'atlasId']);
queryInterface.addIndex('documents', ['id', 'teamId']);
@@ -33,9 +37,13 @@ module.exports = {
queryInterface.removeIndex('documents', ['urlId', 'deletedAt']);
queryInterface.removeIndex('documents', ['id', 'atlasId', 'deletedAt']);
queryInterface.removeIndex('documents', ['id', 'teamId', 'deletedAt']);
- queryInterface.removeIndex('documents', ['parentDocumentId', 'atlasId', 'deletedAt']);
+ queryInterface.removeIndex('documents', [
+ 'parentDocumentId',
+ 'atlasId',
+ 'deletedAt',
+ ]);
queryInterface.removeIndex('atlases', ['id', 'deletedAt']);
queryInterface.removeIndex('atlases', ['id', 'teamId', 'deletedAt']);
- }
+ },
};
diff --git a/server/migrations/20160814095336-add-document-createdById.js b/server/migrations/20160814095336-add-document-createdById.js
index 9228addb5..1c53a0699 100644
--- a/server/migrations/20160814095336-add-document-createdById.js
+++ b/server/migrations/20160814095336-add-document-createdById.js
@@ -1,21 +1,17 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.addColumn(
- 'documents',
- 'createdById',
- {
- type: 'UUID',
- allowNull: true,
- references: {
- model: 'users',
- },
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.addColumn('documents', 'createdById', {
+ type: 'UUID',
+ allowNull: true,
+ references: {
+ model: 'users',
+ },
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeColumn('documents', 'createdById');
},
};
diff --git a/server/migrations/20160814111419-add-document-collaboratorIds.js b/server/migrations/20160814111419-add-document-collaboratorIds.js
index b15315fd2..da7332089 100644
--- a/server/migrations/20160814111419-add-document-collaboratorIds.js
+++ b/server/migrations/20160814111419-add-document-collaboratorIds.js
@@ -1,14 +1,12 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.addColumn(
- 'documents',
- 'collaboratorIds',
- { type: Sequelize.ARRAY(Sequelize.UUID) }
- )
+ up: function(queryInterface, Sequelize) {
+ queryInterface.addColumn('documents', 'collaboratorIds', {
+ type: Sequelize.ARRAY(Sequelize.UUID),
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeColumn('documents', 'collaboratorIds');
},
};
diff --git a/server/migrations/20160815142720-app-collection-urlId.js b/server/migrations/20160815142720-app-collection-urlId.js
index bd9c1aeff..bb8b0c708 100644
--- a/server/migrations/20160815142720-app-collection-urlId.js
+++ b/server/migrations/20160815142720-app-collection-urlId.js
@@ -1,18 +1,14 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.addColumn(
- 'atlases',
- 'urlId',
- {
- type: Sequelize.STRING,
- unique: true,
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.addColumn('atlases', 'urlId', {
+ type: Sequelize.STRING,
+ unique: true,
+ });
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeColumn('atlases', 'urlId');
- }
+ },
};
diff --git a/server/migrations/20160816082738-add-revision-index.js b/server/migrations/20160816082738-add-revision-index.js
index 4206bb594..2ac17f658 100644
--- a/server/migrations/20160816082738-add-revision-index.js
+++ b/server/migrations/20160816082738-add-revision-index.js
@@ -1,11 +1,11 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
queryInterface.addIndex('revisions', ['documentId']);
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeIndex('revisions', ['documentId']);
},
};
diff --git a/server/migrations/20160824061730-add-apikeys.js b/server/migrations/20160824061730-add-apikeys.js
index 7cff90f67..f12f14050 100644
--- a/server/migrations/20160824061730-add-apikeys.js
+++ b/server/migrations/20160824061730-add-apikeys.js
@@ -1,7 +1,7 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
queryInterface.createTable('apiKeys', {
id: {
type: 'UUID',
@@ -40,7 +40,7 @@ module.exports = {
});
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.createTable('apiKeys');
},
};
diff --git a/server/migrations/20160824062457-add-apikey-indeces.js b/server/migrations/20160824062457-add-apikey-indeces.js
index 1ed8ae22b..c30226a59 100644
--- a/server/migrations/20160824062457-add-apikey-indeces.js
+++ b/server/migrations/20160824062457-add-apikey-indeces.js
@@ -1,12 +1,12 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
+ up: function(queryInterface, Sequelize) {
queryInterface.addIndex('apiKeys', ['secret', 'deletedAt']);
queryInterface.addIndex('apiKeys', ['userId', 'deletedAt']);
},
- down: function (queryInterface, Sequelize) {
+ down: function(queryInterface, Sequelize) {
queryInterface.removeIndex('apiKeys', ['secret', 'deletedAt']);
queryInterface.removeIndex('apiKeys', ['userId', 'deletedAt']);
},
diff --git a/server/migrations/20160911230444-user-optional-slack-id.js b/server/migrations/20160911230444-user-optional-slack-id.js
index b1bf1f188..f3531738e 100644
--- a/server/migrations/20160911230444-user-optional-slack-id.js
+++ b/server/migrations/20160911230444-user-optional-slack-id.js
@@ -2,45 +2,29 @@
'use strict';
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.changeColumn(
- 'users',
- 'slackId',
- {
- type: Sequelize.STRING,
- unique: false,
- allowNull: true,
- }
- );
- queryInterface.changeColumn(
- 'teams',
- 'slackId',
- {
- type: Sequelize.STRING,
- unique: false,
- allowNull: true,
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.changeColumn('users', 'slackId', {
+ type: Sequelize.STRING,
+ unique: false,
+ allowNull: true,
+ });
+ queryInterface.changeColumn('teams', 'slackId', {
+ type: Sequelize.STRING,
+ unique: false,
+ allowNull: true,
+ });
},
- down: function (queryInterface, Sequelize) {
- queryInterface.changeColumn(
- 'users',
- 'slackId',
- {
- type: Sequelize.STRING,
- unique: true,
- allowNull: false,
- }
- );
- queryInterface.changeColumn(
- 'teams',
- 'slackId',
- {
- type: Sequelize.STRING,
- unique: true,
- allowNull: false,
- }
- );
+ down: function(queryInterface, Sequelize) {
+ queryInterface.changeColumn('users', 'slackId', {
+ type: Sequelize.STRING,
+ unique: true,
+ allowNull: false,
+ });
+ queryInterface.changeColumn('teams', 'slackId', {
+ type: Sequelize.STRING,
+ unique: true,
+ allowNull: false,
+ });
},
};
diff --git a/server/migrations/20160911232911-user-unique-fields.js b/server/migrations/20160911232911-user-unique-fields.js
index 5786ff4d0..4dfaffe24 100644
--- a/server/migrations/20160911232911-user-unique-fields.js
+++ b/server/migrations/20160911232911-user-unique-fields.js
@@ -1,44 +1,28 @@
module.exports = {
- up: function (queryInterface, Sequelize) {
- queryInterface.changeColumn(
- 'users',
- 'email',
- {
- type: Sequelize.STRING,
- unique: true,
- allowNull: false,
- }
- );
- queryInterface.changeColumn(
- 'users',
- 'username',
- {
- type: Sequelize.STRING,
- unique: true,
- allowNull: false,
- }
- );
+ up: function(queryInterface, Sequelize) {
+ queryInterface.changeColumn('users', 'email', {
+ type: Sequelize.STRING,
+ unique: true,
+ allowNull: false,
+ });
+ queryInterface.changeColumn('users', 'username', {
+ type: Sequelize.STRING,
+ unique: true,
+ allowNull: false,
+ });
},
- down: function (queryInterface, Sequelize) {
- queryInterface.changeColumn(
- 'users',
- 'email',
- {
- type: Sequelize.STRING,
- unique: false,
- allowNull: true,
- }
- );
+ down: function(queryInterface, Sequelize) {
+ queryInterface.changeColumn('users', 'email', {
+ type: Sequelize.STRING,
+ unique: false,
+ allowNull: true,
+ });
- queryInterface.changeColumn(
- 'users',
- 'username',
- {
- type: Sequelize.STRING,
- unique: false,
- allowNull: true,
- }
- );
+ queryInterface.changeColumn('users', 'username', {
+ type: Sequelize.STRING,
+ unique: false,
+ allowNull: true,
+ });
},
};
diff --git a/server/models/ApiKey.js b/server/models/ApiKey.js
index f54636a92..69ade74d4 100644
--- a/server/models/ApiKey.js
+++ b/server/models/ApiKey.js
@@ -1,28 +1,33 @@
-import {
- DataTypes,
- sequelize,
-} from '../sequelize';
+import { DataTypes, sequelize } from '../sequelize';
import randomstring from 'randomstring';
-const Team = sequelize.define('team', {
- id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
- name: DataTypes.STRING,
- secret: { type: DataTypes.STRING, unique: true },
- userId: {
- type: DataTypes.UUID,
- allowNull: false,
- references: {
- model: 'users',
+const Team = sequelize.define(
+ 'team',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+ name: DataTypes.STRING,
+ secret: { type: DataTypes.STRING, unique: true },
+ userId: {
+ type: DataTypes.UUID,
+ allowNull: false,
+ references: {
+ model: 'users',
+ },
},
},
-}, {
- tableName: 'apiKeys',
- paranoid: true,
- hooks: {
- beforeValidate: (key) => {
- key.secret = randomstring.generate(38);
+ {
+ tableName: 'apiKeys',
+ paranoid: true,
+ hooks: {
+ beforeValidate: key => {
+ key.secret = randomstring.generate(38);
+ },
},
- },
-});
+ }
+);
export default Team;
diff --git a/server/models/Atlas.js b/server/models/Atlas.js
index 57a537409..c78804585 100644
--- a/server/models/Atlas.js
+++ b/server/models/Atlas.js
@@ -1,9 +1,6 @@
import slug from 'slug';
import randomstring from 'randomstring';
-import {
- DataTypes,
- sequelize,
-} from '../sequelize';
+import { DataTypes, sequelize } from '../sequelize';
import _ from 'lodash';
import Document from './Document';
@@ -11,184 +8,207 @@ slug.defaults.mode = 'rfc3986';
const allowedAtlasTypes = [['atlas', 'journal']];
-const Atlas = sequelize.define('atlas', {
- id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
- urlId: { type: DataTypes.STRING, unique: true },
- name: DataTypes.STRING,
- description: DataTypes.STRING,
- type: { type: DataTypes.STRING, validate: { isIn: allowedAtlasTypes } },
- creatorId: DataTypes.UUID,
-
- /* type: atlas */
- navigationTree: DataTypes.JSONB,
-}, {
- tableName: 'atlases',
- paranoid: true,
- hooks: {
- beforeValidate: (collection) => {
- collection.urlId = collection.urlId || randomstring.generate(10);
+const Atlas = sequelize.define(
+ 'atlas',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
},
- afterCreate: async (collection) => {
- if (collection.type !== 'atlas') return;
+ urlId: { type: DataTypes.STRING, unique: true },
+ name: DataTypes.STRING,
+ description: DataTypes.STRING,
+ type: { type: DataTypes.STRING, validate: { isIn: allowedAtlasTypes } },
+ creatorId: DataTypes.UUID,
- await Document.create({
- parentDocumentId: null,
- atlasId: collection.id,
- teamId: collection.teamId,
- userId: collection.creatorId,
- lastModifiedById: collection.creatorId,
- createdById: collection.creatorId,
- title: 'Introduction',
- text: '# Introduction\n\nLets get started...',
- });
- await collection.buildStructure();
- await collection.save();
- },
+ /* type: atlas */
+ navigationTree: DataTypes.JSONB,
},
- instanceMethods: {
- getUrl() {
- // const slugifiedName = slug(this.name);
- // return `/${slugifiedName}-c${this.urlId}`;
- return `/collections/${this.id}`;
- },
- async buildStructure() {
- if (this.navigationTree) return this.navigationTree;
+ {
+ tableName: 'atlases',
+ paranoid: true,
+ hooks: {
+ beforeValidate: collection => {
+ collection.urlId = collection.urlId || randomstring.generate(10);
+ },
+ afterCreate: async collection => {
+ if (collection.type !== 'atlas') return;
- const getNodeForDocument = async (document) => {
- const children = await Document.findAll({ where: {
- parentDocumentId: document.id,
- atlasId: this.id,
- } });
-
- const childNodes = [];
- await Promise.all(children.map(async (child) => {
- return childNodes.push(await getNodeForDocument(child));
- }));
-
- return {
- title: document.title,
- id: document.id,
- url: document.getUrl(),
- children: childNodes,
- };
- };
-
- const rootDocument = await Document.findOne({
- where: {
+ await Document.create({
parentDocumentId: null,
- atlasId: this.id,
- },
- });
-
- this.navigationTree = await getNodeForDocument(rootDocument);
- return this.navigationTree;
+ atlasId: collection.id,
+ teamId: collection.teamId,
+ userId: collection.creatorId,
+ lastModifiedById: collection.creatorId,
+ createdById: collection.creatorId,
+ title: 'Introduction',
+ text: '# Introduction\n\nLets get started...',
+ });
+ await collection.buildStructure();
+ await collection.save();
+ },
},
- async updateNavigationTree(tree = this.navigationTree) {
- const nodeIds = [];
- nodeIds.push(tree.id);
+ instanceMethods: {
+ getUrl() {
+ // const slugifiedName = slug(this.name);
+ // return `/${slugifiedName}-c${this.urlId}`;
+ return `/collections/${this.id}`;
+ },
+ async buildStructure() {
+ if (this.navigationTree) return this.navigationTree;
- const rootDocument = await Document.findOne({
- where: {
- id: tree.id,
- atlasId: this.id,
- },
- });
- if (!rootDocument) throw new Error;
-
- const newTree = {
- id: tree.id,
- title: rootDocument.title,
- url: rootDocument.getUrl(),
- children: [],
- };
-
- const getIdsForChildren = async (children) => {
- const childNodes = [];
- for (const child of children) {
- const childDocument = await Document.findOne({
+ const getNodeForDocument = async document => {
+ const children = await Document.findAll({
where: {
- id: child.id,
+ parentDocumentId: document.id,
atlasId: this.id,
},
});
- if (childDocument) {
- childNodes.push({
- id: childDocument.id,
- title: childDocument.title,
- url: childDocument.getUrl(),
- children: await getIdsForChildren(child.children),
- });
- nodeIds.push(child.id);
- }
- }
- return childNodes;
- };
- newTree.children = await getIdsForChildren(tree.children);
- const documents = await Document.findAll({
- attributes: ['id'],
- where: {
- atlasId: this.id,
- },
- });
- const documentIds = documents.map(doc => doc.id);
+ const childNodes = [];
+ await Promise.all(
+ children.map(async child => {
+ return childNodes.push(await getNodeForDocument(child));
+ })
+ );
- if (!_.isEqual(nodeIds.sort(), documentIds.sort())) {
- throw new Error('Invalid navigation tree');
- }
+ return {
+ title: document.title,
+ id: document.id,
+ url: document.getUrl(),
+ children: childNodes,
+ };
+ };
- this.navigationTree = newTree;
- await this.save();
-
- return newTree;
- },
- async addNodeToNavigationTree(document) {
- const newNode = {
- id: document.id,
- title: document.title,
- url: document.getUrl(),
- children: [],
- };
-
- const insertNode = (node) => {
- if (document.parentDocumentId === node.id) {
- node.children.push(newNode);
- } else {
- node.children = node.children.map(childNode => {
- return insertNode(childNode);
- });
- }
-
- return node;
- };
-
- this.navigationTree = insertNode(this.navigationTree);
- return this.navigationTree;
- },
- async deleteDocument(document) {
- const deleteNodeAndDocument = async (node, documentId, shouldDelete = false) => {
- // Delete node if id matches
- if (document.id === node.id) shouldDelete = true;
-
- const newChildren = [];
- node.children.forEach(async childNode => {
- const child = await deleteNodeAndDocument(childNode, documentId, shouldDelete);
- if (child) newChildren.push(child);
+ const rootDocument = await Document.findOne({
+ where: {
+ parentDocumentId: null,
+ atlasId: this.id,
+ },
});
- node.children = newChildren;
- if (shouldDelete) {
- const doc = await Document.findById(node.id);
- await doc.destroy();
+ this.navigationTree = await getNodeForDocument(rootDocument);
+ return this.navigationTree;
+ },
+ async updateNavigationTree(tree = this.navigationTree) {
+ const nodeIds = [];
+ nodeIds.push(tree.id);
+
+ const rootDocument = await Document.findOne({
+ where: {
+ id: tree.id,
+ atlasId: this.id,
+ },
+ });
+ if (!rootDocument) throw new Error();
+
+ const newTree = {
+ id: tree.id,
+ title: rootDocument.title,
+ url: rootDocument.getUrl(),
+ children: [],
+ };
+
+ const getIdsForChildren = async children => {
+ const childNodes = [];
+ for (const child of children) {
+ const childDocument = await Document.findOne({
+ where: {
+ id: child.id,
+ atlasId: this.id,
+ },
+ });
+ if (childDocument) {
+ childNodes.push({
+ id: childDocument.id,
+ title: childDocument.title,
+ url: childDocument.getUrl(),
+ children: await getIdsForChildren(child.children),
+ });
+ nodeIds.push(child.id);
+ }
+ }
+ return childNodes;
+ };
+ newTree.children = await getIdsForChildren(tree.children);
+
+ const documents = await Document.findAll({
+ attributes: ['id'],
+ where: {
+ atlasId: this.id,
+ },
+ });
+ const documentIds = documents.map(doc => doc.id);
+
+ if (!_.isEqual(nodeIds.sort(), documentIds.sort())) {
+ throw new Error('Invalid navigation tree');
}
- return shouldDelete ? null : node;
- };
+ this.navigationTree = newTree;
+ await this.save();
- this.navigationTree = await deleteNodeAndDocument(this.navigationTree, document.id);
+ return newTree;
+ },
+ async addNodeToNavigationTree(document) {
+ const newNode = {
+ id: document.id,
+ title: document.title,
+ url: document.getUrl(),
+ children: [],
+ };
+
+ const insertNode = node => {
+ if (document.parentDocumentId === node.id) {
+ node.children.push(newNode);
+ } else {
+ node.children = node.children.map(childNode => {
+ return insertNode(childNode);
+ });
+ }
+
+ return node;
+ };
+
+ this.navigationTree = insertNode(this.navigationTree);
+ return this.navigationTree;
+ },
+ async deleteDocument(document) {
+ const deleteNodeAndDocument = async (
+ node,
+ documentId,
+ shouldDelete = false
+ ) => {
+ // Delete node if id matches
+ if (document.id === node.id) shouldDelete = true;
+
+ const newChildren = [];
+ node.children.forEach(async childNode => {
+ const child = await deleteNodeAndDocument(
+ childNode,
+ documentId,
+ shouldDelete
+ );
+ if (child) newChildren.push(child);
+ });
+ node.children = newChildren;
+
+ if (shouldDelete) {
+ const doc = await Document.findById(node.id);
+ await doc.destroy();
+ }
+
+ return shouldDelete ? null : node;
+ };
+
+ this.navigationTree = await deleteNodeAndDocument(
+ this.navigationTree,
+ document.id
+ );
+ },
},
- },
-});
+ }
+);
Atlas.hasMany(Document, { as: 'documents', foreignKey: 'atlasId' });
diff --git a/server/models/Document.js b/server/models/Document.js
index d5ecb648d..b20a98b30 100644
--- a/server/models/Document.js
+++ b/server/models/Document.js
@@ -1,22 +1,15 @@
import slug from 'slug';
import _ from 'lodash';
import randomstring from 'randomstring';
-import {
- DataTypes,
- sequelize,
-} from '../sequelize';
-import {
- convertToMarkdown,
-} from '../../frontend/utils/markdown';
-import {
- truncateMarkdown,
-} from '../utils/truncate';
+import { DataTypes, sequelize } from '../sequelize';
+import { convertToMarkdown } from '../../frontend/utils/markdown';
+import { truncateMarkdown } from '../utils/truncate';
import User from './User';
import Revision from './Revision';
slug.defaults.mode = 'rfc3986';
-const createRevision = async (doc) => {
+const createRevision = async doc => {
// Create revision of the current (latest)
await Revision.create({
title: doc.title,
@@ -28,7 +21,7 @@ const createRevision = async (doc) => {
});
};
-const documentBeforeSave = async (doc) => {
+const documentBeforeSave = async doc => {
doc.html = convertToMarkdown(doc.text);
doc.preview = truncateMarkdown(doc.text, 160);
@@ -52,50 +45,58 @@ const documentBeforeSave = async (doc) => {
return doc;
};
-const Document = sequelize.define('document', {
- id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
- urlId: { type: DataTypes.STRING, primaryKey: true },
- private: { type: DataTypes.BOOLEAN, defaultValue: true },
- title: DataTypes.STRING,
- text: DataTypes.TEXT,
- html: DataTypes.TEXT,
- preview: DataTypes.TEXT,
- revisionCount: { type: DataTypes.INTEGER, defaultValue: 0 },
+const Document = sequelize.define(
+ 'document',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ },
+ urlId: { type: DataTypes.STRING, primaryKey: true },
+ private: { type: DataTypes.BOOLEAN, defaultValue: true },
+ title: DataTypes.STRING,
+ text: DataTypes.TEXT,
+ html: DataTypes.TEXT,
+ preview: DataTypes.TEXT,
+ revisionCount: { type: DataTypes.INTEGER, defaultValue: 0 },
- parentDocumentId: DataTypes.UUID,
- createdById: {
- type: DataTypes.UUID,
- allowNull: false,
- references: {
- model: 'users',
+ parentDocumentId: DataTypes.UUID,
+ createdById: {
+ type: DataTypes.UUID,
+ allowNull: false,
+ references: {
+ model: 'users',
+ },
},
- },
- lastModifiedById: {
- type: DataTypes.UUID,
- allowNull: false,
- references: {
- model: 'users',
+ lastModifiedById: {
+ type: DataTypes.UUID,
+ allowNull: false,
+ references: {
+ model: 'users',
+ },
},
+ collaboratorIds: DataTypes.ARRAY(DataTypes.UUID),
},
- collaboratorIds: DataTypes.ARRAY(DataTypes.UUID),
-}, {
- paranoid: true,
- hooks: {
- beforeValidate: (doc) => {
- doc.urlId = doc.urlId || randomstring.generate(10);
+ {
+ paranoid: true,
+ hooks: {
+ beforeValidate: doc => {
+ doc.urlId = doc.urlId || randomstring.generate(10);
+ },
+ beforeCreate: documentBeforeSave,
+ beforeUpdate: documentBeforeSave,
+ afterCreate: async doc => await createRevision(doc),
+ afterUpdate: async doc => await createRevision(doc),
},
- beforeCreate: documentBeforeSave,
- beforeUpdate: documentBeforeSave,
- afterCreate: async (doc) => await createRevision(doc),
- afterUpdate: async (doc) => await createRevision(doc),
- },
- instanceMethods: {
- getUrl() {
- const slugifiedTitle = slug(this.title);
- return `/d/${slugifiedTitle}-${this.urlId}`;
+ instanceMethods: {
+ getUrl() {
+ const slugifiedTitle = slug(this.title);
+ return `/d/${slugifiedTitle}-${this.urlId}`;
+ },
},
- },
-});
+ }
+);
Document.belongsTo(User);
@@ -112,18 +113,14 @@ Document.searchForUser = async (user, query, options = {}) => {
LIMIT :limit OFFSET :offset;
`;
- const documents = await sequelize
- .query(
- sql,
- {
- replacements: {
- query,
- limit,
- offset,
- },
- model: Document,
- }
- );
+ const documents = await sequelize.query(sql, {
+ replacements: {
+ query,
+ limit,
+ offset,
+ },
+ model: Document,
+ });
return documents;
};
diff --git a/server/models/Revision.js b/server/models/Revision.js
index e4e17e4af..831078566 100644
--- a/server/models/Revision.js
+++ b/server/models/Revision.js
@@ -1,10 +1,11 @@
-import {
- DataTypes,
- sequelize,
-} from '../sequelize';
+import { DataTypes, sequelize } from '../sequelize';
const Revision = sequelize.define('revision', {
- id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
+ },
title: DataTypes.STRING,
text: DataTypes.TEXT,
html: DataTypes.TEXT,
@@ -15,7 +16,7 @@ const Revision = sequelize.define('revision', {
allowNull: false,
references: {
model: 'users',
- }
+ },
},
documentId: {
@@ -24,7 +25,7 @@ const Revision = sequelize.define('revision', {
references: {
model: 'documents',
onDelete: 'CASCADE',
- }
+ },
},
});
diff --git a/server/models/Team.js b/server/models/Team.js
index ef1f04b02..fba3dff36 100644
--- a/server/models/Team.js
+++ b/server/models/Team.js
@@ -1,36 +1,41 @@
-import {
- DataTypes,
- sequelize,
-} from '../sequelize';
+import { DataTypes, sequelize } from '../sequelize';
import Atlas from './Atlas';
import Document from './Document';
import User from './User';
-const Team = sequelize.define('team', {
- id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
- name: DataTypes.STRING,
- slackId: { type: DataTypes.STRING, allowNull: true },
- slackData: DataTypes.JSONB,
-}, {
- instanceMethods: {
- async createFirstAtlas(userId) {
- const atlas = await Atlas.create({
- name: this.name,
- description: 'Your first Atlas',
- type: 'atlas',
- teamId: this.id,
- creatorId: userId,
- });
- return atlas;
+const Team = sequelize.define(
+ 'team',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
},
+ name: DataTypes.STRING,
+ slackId: { type: DataTypes.STRING, allowNull: true },
+ slackData: DataTypes.JSONB,
},
- indexes: [
- {
- unique: true,
- fields: ['slackId'],
+ {
+ instanceMethods: {
+ async createFirstAtlas(userId) {
+ const atlas = await Atlas.create({
+ name: this.name,
+ description: 'Your first Atlas',
+ type: 'atlas',
+ teamId: this.id,
+ creatorId: userId,
+ });
+ return atlas;
+ },
},
- ],
-});
+ indexes: [
+ {
+ unique: true,
+ fields: ['slackId'],
+ },
+ ],
+ }
+);
Team.hasMany(Atlas, { as: 'atlases' });
Team.hasMany(Document, { as: 'documents' });
diff --git a/server/models/User.js b/server/models/User.js
index 0d6c2b3b5..e2e456815 100644
--- a/server/models/User.js
+++ b/server/models/User.js
@@ -1,61 +1,65 @@
import crypto from 'crypto';
import bcrypt from 'bcrypt';
-import {
- DataTypes,
- sequelize,
- encryptedFields,
-} from '../sequelize';
+import { DataTypes, sequelize, encryptedFields } from '../sequelize';
import JWT from 'jsonwebtoken';
const BCRYPT_COST = process.env.NODE_ENV !== 'production' ? 4 : 12;
-const User = sequelize.define('user', {
- id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
- email: { type: DataTypes.STRING, unique: true },
- username: { type: DataTypes.STRING, unique: true },
- name: DataTypes.STRING,
- password: DataTypes.VIRTUAL,
- passwordDigest: DataTypes.STRING,
- isAdmin: DataTypes.BOOLEAN,
- slackAccessToken: encryptedFields.vault('slackAccessToken'),
- slackId: { type: DataTypes.STRING, allowNull: true },
- slackData: DataTypes.JSONB,
- jwtSecret: encryptedFields.vault('jwtSecret'),
-}, {
- instanceMethods: {
- getJwtToken() {
- return JWT.sign({ id: this.id }, this.jwtSecret);
+const User = sequelize.define(
+ 'user',
+ {
+ id: {
+ type: DataTypes.UUID,
+ defaultValue: DataTypes.UUIDV4,
+ primaryKey: true,
},
- async getTeam() {
- return this.team;
- },
- verifyPassword(password) {
- return new Promise((resolve, reject) => {
- if (!this.passwordDigest) {
- resolve(false);
- return;
- }
-
- bcrypt.compare(password, this.passwordDigest, (err, ok) => {
- if (err) {
- reject(err);
+ email: { type: DataTypes.STRING, unique: true },
+ username: { type: DataTypes.STRING, unique: true },
+ name: DataTypes.STRING,
+ password: DataTypes.VIRTUAL,
+ passwordDigest: DataTypes.STRING,
+ isAdmin: DataTypes.BOOLEAN,
+ slackAccessToken: encryptedFields.vault('slackAccessToken'),
+ slackId: { type: DataTypes.STRING, allowNull: true },
+ slackData: DataTypes.JSONB,
+ jwtSecret: encryptedFields.vault('jwtSecret'),
+ },
+ {
+ instanceMethods: {
+ getJwtToken() {
+ return JWT.sign({ id: this.id }, this.jwtSecret);
+ },
+ async getTeam() {
+ return this.team;
+ },
+ verifyPassword(password) {
+ return new Promise((resolve, reject) => {
+ if (!this.passwordDigest) {
+ resolve(false);
return;
}
- resolve(ok);
- });
- });
- },
- },
- indexes: [
- {
- fields: ['email'],
- },
- ],
-});
+ bcrypt.compare(password, this.passwordDigest, (err, ok) => {
+ if (err) {
+ reject(err);
+ return;
+ }
-const setRandomJwtSecret = (model) => {
+ resolve(ok);
+ });
+ });
+ },
+ },
+ indexes: [
+ {
+ fields: ['email'],
+ },
+ ],
+ }
+);
+
+const setRandomJwtSecret = model => {
model.jwtSecret = crypto.randomBytes(64).toString('hex');
};
const hashPassword = function hashPassword(model) {
diff --git a/server/models/index.js b/server/models/index.js
index db1d5bc05..a3d7e55da 100644
--- a/server/models/index.js
+++ b/server/models/index.js
@@ -5,11 +5,4 @@ import Document from './Document';
import Revision from './Revision';
import ApiKey from './ApiKey';
-export {
- User,
- Team,
- Atlas,
- Document,
- Revision,
- ApiKey,
-};
+export { User, Team, Atlas, Document, Revision, ApiKey };
diff --git a/server/presenters/user.test.js b/server/presenters/user.test.js
index a26c10fdb..672a0a1b1 100644
--- a/server/presenters/user.test.js
+++ b/server/presenters/user.test.js
@@ -3,31 +3,25 @@ import presentUser from './user';
import ctx from '../../__mocks__/ctx';
it('presents a user', async () => {
- const user = await presentUser(
- ctx,
- {
- id: '123',
- name: 'Test User',
- username: 'testuser',
- slackData: {
- image_192: 'http://example.com/avatar.png',
- },
+ const user = await presentUser(ctx, {
+ id: '123',
+ name: 'Test User',
+ username: 'testuser',
+ slackData: {
+ image_192: 'http://example.com/avatar.png',
},
- );
+ });
expect(user).toMatchSnapshot();
});
it('presents a user without slack data', async () => {
- const user = await presentUser(
- ctx,
- {
- id: '123',
- name: 'Test User',
- username: 'testuser',
- slackData: null,
- },
- );
+ const user = await presentUser(ctx, {
+ id: '123',
+ name: 'Test User',
+ username: 'testuser',
+ slackData: null,
+ });
expect(user).toMatchSnapshot();
});
diff --git a/server/static/service-worker.js b/server/static/service-worker.js
index 2adf7d2b7..63502e914 100644
--- a/server/static/service-worker.js
+++ b/server/static/service-worker.js
@@ -16,52 +16,856 @@
limitations under the License.
*/
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.toolbox = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o