[chore] added prettier
This commit is contained in:
parent
fcdeb67bc5
commit
08b1609440
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
<Base
|
||||
{...props}
|
||||
className='Switch'
|
||||
role='checkbox'
|
||||
className="Switch"
|
||||
role="checkbox"
|
||||
aria-checked={checked}
|
||||
baseStyle={sx.root}>
|
||||
baseStyle={sx.root}
|
||||
>
|
||||
<div style={sx.dot} />
|
||||
</Base>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
Switch.propTypes = {
|
||||
/** Sets the Switch to an active style */
|
||||
checked: React.PropTypes.bool
|
||||
}
|
||||
checked: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
Switch.contextTypes = {
|
||||
rebass: React.PropTypes.object
|
||||
}
|
||||
rebass: React.PropTypes.object,
|
||||
};
|
||||
|
||||
export default Switch;
|
||||
|
|
|
@ -2,15 +2,14 @@ import React from 'react';
|
|||
import { observer } from 'mobx-react';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
@observer
|
||||
class Application extends React.Component {
|
||||
@observer class Application extends React.Component {
|
||||
static childContextTypes = {
|
||||
rebass: React.PropTypes.object,
|
||||
}
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
children: React.PropTypes.node.isRequired,
|
||||
}
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
|
@ -25,13 +24,7 @@ class Application extends React.Component {
|
|||
|
||||
// fontSizes: [64, 48, 28, 20, 18, 16, 14],
|
||||
bold: 500,
|
||||
scale: [
|
||||
0,
|
||||
8,
|
||||
18,
|
||||
36,
|
||||
72,
|
||||
],
|
||||
scale: [0, 8, 18, 36, 72],
|
||||
Input: {
|
||||
// borderBottom: '1px solid #eee',
|
||||
},
|
||||
|
@ -43,9 +36,7 @@ class Application extends React.Component {
|
|||
ButtonOutline: {
|
||||
color: '#000',
|
||||
},
|
||||
InlineForm: {
|
||||
|
||||
},
|
||||
InlineForm: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -55,14 +46,14 @@ class Application extends React.Component {
|
|||
<div style={{ width: '100%', height: '100%', display: 'flex', flex: 1 }}>
|
||||
<Helmet
|
||||
title="Atlas"
|
||||
meta={ [
|
||||
meta={[
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1.0',
|
||||
},
|
||||
] }
|
||||
]}
|
||||
/>
|
||||
{ this.props.children }
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -21,13 +21,13 @@ class CacheStore {
|
|||
_.defer(() => localStorage.setItem(CACHE_STORE, this.asJson));
|
||||
};
|
||||
|
||||
@action cacheList = (data) => {
|
||||
data.forEach((item) => this.cacheWithId(item.id, item));
|
||||
@action cacheList = data => {
|
||||
data.forEach(item => this.cacheWithId(item.id, item));
|
||||
};
|
||||
|
||||
@action fetchFromCache = (id) => {
|
||||
@action fetchFromCache = id => {
|
||||
return this.cache[id];
|
||||
}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
// Rehydrate
|
||||
|
@ -37,6 +37,4 @@ class CacheStore {
|
|||
}
|
||||
|
||||
export default CacheStore;
|
||||
export {
|
||||
CACHE_STORE,
|
||||
};
|
||||
export { CACHE_STORE };
|
||||
|
|
|
@ -27,6 +27,4 @@ class UiStore {
|
|||
}
|
||||
|
||||
export default UiStore;
|
||||
export {
|
||||
UI_STORE,
|
||||
};
|
||||
export { UI_STORE };
|
||||
|
|
|
@ -40,7 +40,7 @@ class UserStore {
|
|||
const state = Math.random().toString(36).substring(7);
|
||||
this.oauthState = state;
|
||||
return this.oauthState;
|
||||
}
|
||||
};
|
||||
|
||||
@action authWithSlack = async (code, state, redirectTo) => {
|
||||
if (state !== this.oauthState) {
|
||||
|
@ -60,7 +60,7 @@ class UserStore {
|
|||
this.team = res.data.team;
|
||||
this.token = res.data.accessToken;
|
||||
browserHistory.replace(redirectTo || '/');
|
||||
}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
// Rehydrate
|
||||
|
@ -73,6 +73,4 @@ class UserStore {
|
|||
}
|
||||
|
||||
export default UserStore;
|
||||
export {
|
||||
USER_STORE,
|
||||
};
|
||||
export { USER_STORE };
|
||||
|
|
|
@ -7,7 +7,7 @@ import constants from '../constants';
|
|||
const isIterable = object =>
|
||||
object != null && typeof object[Symbol.iterator] === 'function';
|
||||
|
||||
const cacheResponse = (data) => {
|
||||
const cacheResponse = data => {
|
||||
if (isIterable(data)) {
|
||||
stores.cache.cacheList(data);
|
||||
} else {
|
||||
|
@ -51,59 +51,58 @@ class ApiClient {
|
|||
// Handle request promises and return a new promise
|
||||
return new Promise((resolve, reject) => {
|
||||
request
|
||||
.then((response) => {
|
||||
// Handle successful responses
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
.then(response => {
|
||||
// Handle successful responses
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Handle 404
|
||||
if (response.status === 404) {
|
||||
return browserHistory.push('/404');
|
||||
}
|
||||
// Handle 404
|
||||
if (response.status === 404) {
|
||||
return browserHistory.push('/404');
|
||||
}
|
||||
|
||||
// Handle 401, log out user
|
||||
if (response.status === 401) {
|
||||
return stores.user.logout();
|
||||
}
|
||||
// Handle 401, log out user
|
||||
if (response.status === 401) {
|
||||
return stores.user.logout();
|
||||
}
|
||||
|
||||
// Handle failed responses
|
||||
const error = {};
|
||||
error.statusCode = response.status;
|
||||
error.response = response;
|
||||
throw error;
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((json) => {
|
||||
// Cache responses
|
||||
if (options.cache) {
|
||||
cacheResponse(json.data);
|
||||
}
|
||||
resolve(json);
|
||||
})
|
||||
.catch(error => {
|
||||
error.response.json()
|
||||
// Handle failed responses
|
||||
const error = {};
|
||||
error.statusCode = response.status;
|
||||
error.response = response;
|
||||
throw error;
|
||||
})
|
||||
.then(response => {
|
||||
return response.json();
|
||||
})
|
||||
.then(json => {
|
||||
error.data = json;
|
||||
reject(error);
|
||||
// Cache responses
|
||||
if (options.cache) {
|
||||
cacheResponse(json.data);
|
||||
}
|
||||
resolve(json);
|
||||
})
|
||||
.catch(error => {
|
||||
error.response.json().then(json => {
|
||||
error.data = json;
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
get = (path, data, options) => {
|
||||
return this.fetch(path, 'GET', data, options);
|
||||
}
|
||||
};
|
||||
|
||||
post = (path, data, options) => {
|
||||
return this.fetch(path, 'POST', data, options);
|
||||
}
|
||||
};
|
||||
|
||||
// Helpers
|
||||
|
||||
constructQueryString = (data) => {
|
||||
constructQueryString = data => {
|
||||
return _.map(data, (v, k) => {
|
||||
return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`;
|
||||
}).join('&');
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
export default (type, ...argNames) => {
|
||||
return function(...args) {
|
||||
let action = { type }
|
||||
let action = { type };
|
||||
argNames.forEach((arg, index) => {
|
||||
action[argNames[index]] = args[index]
|
||||
})
|
||||
return action
|
||||
}
|
||||
}
|
||||
action[argNames[index]] = args[index];
|
||||
});
|
||||
return action;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import emojiMapping from './emoji-mapping.json';
|
|||
|
||||
const EMOJI_REGEX = /:([A-Za-z0-9_\-\+]+?):/gm;
|
||||
|
||||
const emojify = (text='') => {
|
||||
const emojify = (text = '') => {
|
||||
const emojis = text.match(EMOJI_REGEX) || [];
|
||||
let emojifiedText = text;
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@ const Renderer = sanitizedRenderer(marked.Renderer);
|
|||
const renderer = new Renderer();
|
||||
renderer.code = (code, language) => {
|
||||
const validLang = !!(language && highlight.getLanguage(language));
|
||||
const highlighted = validLang ? highlight.highlight(language, code).value : _.escape(code);
|
||||
const highlighted = validLang
|
||||
? highlight.highlight(language, code).value
|
||||
: _.escape(code);
|
||||
return `<pre><code class="hljs ${_.escape(language)}">${highlighted}</code></pre>`;
|
||||
};
|
||||
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 };
|
||||
|
|
|
@ -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
|
||||
};
|
||||
export { randomInteger };
|
||||
|
|
|
@ -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 };
|
||||
|
|
14
package.json
14
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"
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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']);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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');
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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']);
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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' });
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -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' });
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
"use strict";function debug(e,n){n=n||{};var t=n.debug||globalOptions.debug;t&&console.log("[sw-toolbox] "+e)}function openCache(e){var n;return e&&e.cache&&(n=e.cache.name),n=n||globalOptions.cache.name,caches.open(n)}function fetchAndCache(e,n){n=n||{};var t=n.successResponses||globalOptions.successResponses;return fetch(e.clone()).then(function(c){return"GET"===e.method&&t.test(c.status)&&openCache(n).then(function(t){t.put(e,c).then(function(){var c=n.cache||globalOptions.cache;(c.maxEntries||c.maxAgeSeconds)&&c.name&&queueCacheExpiration(e,t,c)})}),c.clone()})}function queueCacheExpiration(e,n,t){var c=cleanupCache.bind(null,e,n,t);cleanupQueue=cleanupQueue?cleanupQueue.then(c):c()}function cleanupCache(e,n,t){var c=e.url,a=t.maxAgeSeconds,u=t.maxEntries,o=t.name,r=Date.now();return debug("Updating LRU order for "+c+". Max entries is "+u+", max age is "+a),idbCacheExpiration.getDb(o).then(function(e){return idbCacheExpiration.setTimestampForUrl(e,c,r)}).then(function(e){return idbCacheExpiration.expireEntries(e,u,a,r)}).then(function(e){debug("Successfully updated IDB.");var t=e.map(function(e){return n["delete"](e)});return Promise.all(t).then(function(){debug("Done with cache cleanup.")})})["catch"](function(e){debug(e)})}function renameCache(e,n,t){return debug("Renaming cache: ["+e+"] to ["+n+"]",t),caches["delete"](n).then(function(){return Promise.all([caches.open(e),caches.open(n)]).then(function(n){var t=n[0],c=n[1];return t.keys().then(function(e){return Promise.all(e.map(function(e){return t.match(e).then(function(n){return c.put(e,n)})}))}).then(function(){return caches["delete"](e)})})})}var globalOptions=require("./options"),idbCacheExpiration=require("./idb-cache-expiration"),cleanupQueue;module.exports={debug:debug,fetchAndCache:fetchAndCache,openCache:openCache,renameCache:renameCache};
|
||||
},{"./idb-cache-expiration":2,"./options":3}],2:[function(require,module,exports){
|
||||
"use strict";function openDb(e){return new Promise(function(r,n){var t=indexedDB.open(DB_PREFIX+e,DB_VERSION);t.onupgradeneeded=function(){var e=t.result.createObjectStore(STORE_NAME,{keyPath:URL_PROPERTY});e.createIndex(TIMESTAMP_PROPERTY,TIMESTAMP_PROPERTY,{unique:!1})},t.onsuccess=function(){r(t.result)},t.onerror=function(){n(t.error)}})}function getDb(e){return e in cacheNameToDbPromise||(cacheNameToDbPromise[e]=openDb(e)),cacheNameToDbPromise[e]}function setTimestampForUrl(e,r,n){return new Promise(function(t,o){var i=e.transaction(STORE_NAME,"readwrite"),u=i.objectStore(STORE_NAME);u.put({url:r,timestamp:n}),i.oncomplete=function(){t(e)},i.onabort=function(){o(i.error)}})}function expireOldEntries(e,r,n){return r?new Promise(function(t,o){var i=1e3*r,u=[],c=e.transaction(STORE_NAME,"readwrite"),s=c.objectStore(STORE_NAME),a=s.index(TIMESTAMP_PROPERTY);a.openCursor().onsuccess=function(e){var r=e.target.result;if(r&&n-i>r.value[TIMESTAMP_PROPERTY]){var t=r.value[URL_PROPERTY];u.push(t),s["delete"](t),r["continue"]()}},c.oncomplete=function(){t(u)},c.onabort=o}):Promise.resolve([])}function expireExtraEntries(e,r){return r?new Promise(function(n,t){var o=[],i=e.transaction(STORE_NAME,"readwrite"),u=i.objectStore(STORE_NAME),c=u.index(TIMESTAMP_PROPERTY),s=c.count();c.count().onsuccess=function(){var e=s.result;e>r&&(c.openCursor().onsuccess=function(n){var t=n.target.result;if(t){var i=t.value[URL_PROPERTY];o.push(i),u["delete"](i),e-o.length>r&&t["continue"]()}})},i.oncomplete=function(){n(o)},i.onabort=t}):Promise.resolve([])}function expireEntries(e,r,n,t){return expireOldEntries(e,n,t).then(function(n){return expireExtraEntries(e,r).then(function(e){return n.concat(e)})})}var DB_PREFIX="sw-toolbox-",DB_VERSION=1,STORE_NAME="store",URL_PROPERTY="url",TIMESTAMP_PROPERTY="timestamp",cacheNameToDbPromise={};module.exports={getDb:getDb,setTimestampForUrl:setTimestampForUrl,expireEntries:expireEntries};
|
||||
},{}],3:[function(require,module,exports){
|
||||
"use strict";var scope;scope=self.registration?self.registration.scope:self.scope||new URL("./",self.location).href,module.exports={cache:{name:"$$$toolbox-cache$$$"+scope+"$$$",maxAgeSeconds:null,maxEntries:null},debug:!1,networkTimeoutSeconds:null,preCacheItems:[],successResponses:/^0|([123]\d\d)|(40[14567])|410$/};
|
||||
},{}],4:[function(require,module,exports){
|
||||
"use strict";var url=new URL("./",self.location),basePath=url.pathname,pathRegexp=require("path-to-regexp"),Route=function(e,t,i,s){t instanceof RegExp?this.fullUrlRegExp=t:(0!==t.indexOf("/")&&(t=basePath+t),this.keys=[],this.regexp=pathRegexp(t,this.keys)),this.method=e,this.options=s,this.handler=i};Route.prototype.makeHandler=function(e){var t;if(this.regexp){var i=this.regexp.exec(e);t={},this.keys.forEach(function(e,s){t[e.name]=i[s+1]})}return function(e){return this.handler(e,t,this.options)}.bind(this)},module.exports=Route;
|
||||
},{"path-to-regexp":13}],5:[function(require,module,exports){
|
||||
"use strict";function regexEscape(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var Route=require("./route"),keyMatch=function(e,t){for(var r=e.entries(),o=r.next();!o.done;){var n=new RegExp(o.value[0]);if(n.test(t))return o.value[1];o=r.next()}return null},Router=function(){this.routes=new Map,this["default"]=null};["get","post","put","delete","head","any"].forEach(function(e){Router.prototype[e]=function(t,r,o){return this.add(e,t,r,o)}}),Router.prototype.add=function(e,t,r,o){o=o||{};var n;t instanceof RegExp?n=RegExp:(n=o.origin||self.location.origin,n=n instanceof RegExp?n.source:regexEscape(n)),e=e.toLowerCase();var u=new Route(e,t,r,o);this.routes.has(n)||this.routes.set(n,new Map);var a=this.routes.get(n);a.has(e)||a.set(e,new Map);var s=a.get(e),i=u.regexp||u.fullUrlRegExp;s.set(i.source,u)},Router.prototype.matchMethod=function(e,t){var r=new URL(t),o=r.origin,n=r.pathname;return this._match(e,keyMatch(this.routes,o),n)||this._match(e,this.routes.get(RegExp),t)},Router.prototype._match=function(e,t,r){if(t){var o=t.get(e.toLowerCase());if(o){var n=keyMatch(o,r);if(n)return n.makeHandler(r)}}return null},Router.prototype.match=function(e){return this.matchMethod(e.method,e.url)||this.matchMethod("any",e.url)},module.exports=new Router;
|
||||
},{"./route":4}],6:[function(require,module,exports){
|
||||
"use strict";function cacheFirst(e,r,t){return helpers.debug("Strategy: cache first ["+e.url+"]",t),helpers.openCache(t).then(function(r){return r.match(e).then(function(r){return r?r:helpers.fetchAndCache(e,t)})})}var helpers=require("../helpers");module.exports=cacheFirst;
|
||||
},{"../helpers":1}],7:[function(require,module,exports){
|
||||
"use strict";function cacheOnly(e,r,c){return helpers.debug("Strategy: cache only ["+e.url+"]",c),helpers.openCache(c).then(function(r){return r.match(e)})}var helpers=require("../helpers");module.exports=cacheOnly;
|
||||
},{"../helpers":1}],8:[function(require,module,exports){
|
||||
"use strict";function fastest(e,n,t){return helpers.debug("Strategy: fastest ["+e.url+"]",t),new Promise(function(r,s){var c=!1,o=[],a=function(e){o.push(e.toString()),c?s(new Error('Both cache and network failed: "'+o.join('", "')+'"')):c=!0},h=function(e){e instanceof Response?r(e):a("No result returned")};helpers.fetchAndCache(e.clone(),t).then(h,a),cacheOnly(e,n,t).then(h,a)})}var helpers=require("../helpers"),cacheOnly=require("./cacheOnly");module.exports=fastest;
|
||||
},{"../helpers":1,"./cacheOnly":7}],9:[function(require,module,exports){
|
||||
module.exports={networkOnly:require("./networkOnly"),networkFirst:require("./networkFirst"),cacheOnly:require("./cacheOnly"),cacheFirst:require("./cacheFirst"),fastest:require("./fastest")};
|
||||
},{"./cacheFirst":6,"./cacheOnly":7,"./fastest":8,"./networkFirst":10,"./networkOnly":11}],10:[function(require,module,exports){
|
||||
"use strict";function networkFirst(e,r,t){t=t||{};var s=t.successResponses||globalOptions.successResponses,n=t.networkTimeoutSeconds||globalOptions.networkTimeoutSeconds;return helpers.debug("Strategy: network first ["+e.url+"]",t),helpers.openCache(t).then(function(r){var o,u,c=[];if(n){var i=new Promise(function(t){o=setTimeout(function(){r.match(e).then(function(e){e&&t(e)})},1e3*n)});c.push(i)}var a=helpers.fetchAndCache(e,t).then(function(e){if(o&&clearTimeout(o),s.test(e.status))return e;throw helpers.debug("Response was an HTTP error: "+e.statusText,t),u=e,new Error("Bad response")})["catch"](function(){return helpers.debug("Network or response error, fallback to cache ["+e.url+"]",t),r.match(e).then(function(e){return e||u})});return c.push(a),Promise.race(c)})}var globalOptions=require("../options"),helpers=require("../helpers");module.exports=networkFirst;
|
||||
},{"../helpers":1,"../options":3}],11:[function(require,module,exports){
|
||||
"use strict";function networkOnly(e,r,t){return helpers.debug("Strategy: network only ["+e.url+"]",t),fetch(e)}var helpers=require("../helpers");module.exports=networkOnly;
|
||||
},{"../helpers":1}],12:[function(require,module,exports){
|
||||
"use strict";function cache(e,t){return helpers.openCache(t).then(function(t){return t.add(e)})}function uncache(e,t){return helpers.openCache(t).then(function(t){return t["delete"](e)})}function precache(e){Array.isArray(e)||(e=[e]),options.preCacheItems=options.preCacheItems.concat(e)}require("serviceworker-cache-polyfill");var options=require("./options"),router=require("./router"),helpers=require("./helpers"),strategies=require("./strategies");helpers.debug("Service Worker Toolbox is loading");var flatten=function(e){return e.reduce(function(e,t){return e.concat(t)},[])};self.addEventListener("install",function(e){var t=options.cache.name+"$$$inactive$$$";helpers.debug("install event fired"),helpers.debug("creating cache ["+t+"]"),e.waitUntil(helpers.openCache({cache:{name:t}}).then(function(e){return Promise.all(options.preCacheItems).then(flatten).then(function(t){return helpers.debug("preCache list: "+(t.join(", ")||"(none)")),e.addAll(t)})}))}),self.addEventListener("activate",function(e){helpers.debug("activate event fired");var t=options.cache.name+"$$$inactive$$$";e.waitUntil(helpers.renameCache(t,options.cache.name))}),self.addEventListener("fetch",function(e){var t=router.match(e.request);t?e.respondWith(t(e.request)):router["default"]&&"GET"===e.request.method&&e.respondWith(router["default"](e.request))}),module.exports={networkOnly:strategies.networkOnly,networkFirst:strategies.networkFirst,cacheOnly:strategies.cacheOnly,cacheFirst:strategies.cacheFirst,fastest:strategies.fastest,router:router,options:options,cache:cache,uncache:uncache,precache:precache};
|
||||
},{"./helpers":1,"./options":3,"./router":5,"./strategies":9,"serviceworker-cache-polyfill":15}],13:[function(require,module,exports){
|
||||
function parse(e){for(var t,r=[],n=0,o=0,p="";null!=(t=PATH_REGEXP.exec(e));){var a=t[0],i=t[1],s=t.index;if(p+=e.slice(o,s),o=s+a.length,i)p+=i[1];else{p&&(r.push(p),p="");var u=t[2],c=t[3],l=t[4],f=t[5],g=t[6],x=t[7],h="+"===g||"*"===g,m="?"===g||"*"===g,y=u||"/",T=l||f||(x?".*":"[^"+y+"]+?");r.push({name:c||n++,prefix:u||"",delimiter:y,optional:m,repeat:h,pattern:escapeGroup(T)})}}return o<e.length&&(p+=e.substr(o)),p&&r.push(p),r}function compile(e){return tokensToFunction(parse(e))}function tokensToFunction(e){for(var t=new Array(e.length),r=0;r<e.length;r++)"object"==typeof e[r]&&(t[r]=new RegExp("^"+e[r].pattern+"$"));return function(r){for(var n="",o=r||{},p=0;p<e.length;p++){var a=e[p];if("string"!=typeof a){var i,s=o[a.name];if(null==s){if(a.optional)continue;throw new TypeError('Expected "'+a.name+'" to be defined')}if(isarray(s)){if(!a.repeat)throw new TypeError('Expected "'+a.name+'" to not repeat, but received "'+s+'"');if(0===s.length){if(a.optional)continue;throw new TypeError('Expected "'+a.name+'" to not be empty')}for(var u=0;u<s.length;u++){if(i=encodeURIComponent(s[u]),!t[p].test(i))throw new TypeError('Expected all "'+a.name+'" to match "'+a.pattern+'", but received "'+i+'"');n+=(0===u?a.prefix:a.delimiter)+i}}else{if(i=encodeURIComponent(s),!t[p].test(i))throw new TypeError('Expected "'+a.name+'" to match "'+a.pattern+'", but received "'+i+'"');n+=a.prefix+i}}else n+=a}return n}}function escapeString(e){return e.replace(/([.+*?=^!:${}()[\]|\/])/g,"\\$1")}function escapeGroup(e){return e.replace(/([=!:$\/()])/g,"\\$1")}function attachKeys(e,t){return e.keys=t,e}function flags(e){return e.sensitive?"":"i"}function regexpToRegexp(e,t){var r=e.source.match(/\((?!\?)/g);if(r)for(var n=0;n<r.length;n++)t.push({name:n,prefix:null,delimiter:null,optional:!1,repeat:!1,pattern:null});return attachKeys(e,t)}function arrayToRegexp(e,t,r){for(var n=[],o=0;o<e.length;o++)n.push(pathToRegexp(e[o],t,r).source);var p=new RegExp("(?:"+n.join("|")+")",flags(r));return attachKeys(p,t)}function stringToRegexp(e,t,r){for(var n=parse(e),o=tokensToRegExp(n,r),p=0;p<n.length;p++)"string"!=typeof n[p]&&t.push(n[p]);return attachKeys(o,t)}function tokensToRegExp(e,t){t=t||{};for(var r=t.strict,n=t.end!==!1,o="",p=e[e.length-1],a="string"==typeof p&&/\/$/.test(p),i=0;i<e.length;i++){var s=e[i];if("string"==typeof s)o+=escapeString(s);else{var u=escapeString(s.prefix),c=s.pattern;s.repeat&&(c+="(?:"+u+c+")*"),c=s.optional?u?"(?:"+u+"("+c+"))?":"("+c+")?":u+"("+c+")",o+=c}}return r||(o=(a?o.slice(0,-2):o)+"(?:\\/(?=$))?"),o+=n?"$":r&&a?"":"(?=\\/|$)",new RegExp("^"+o,flags(t))}function pathToRegexp(e,t,r){return t=t||[],isarray(t)?r||(r={}):(r=t,t=[]),e instanceof RegExp?regexpToRegexp(e,t,r):isarray(e)?arrayToRegexp(e,t,r):stringToRegexp(e,t,r)}var isarray=require("isarray");module.exports=pathToRegexp,module.exports.parse=parse,module.exports.compile=compile,module.exports.tokensToFunction=tokensToFunction,module.exports.tokensToRegExp=tokensToRegExp;var PATH_REGEXP=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))"].join("|"),"g");
|
||||
},{"isarray":14}],14:[function(require,module,exports){
|
||||
module.exports=Array.isArray||function(r){return"[object Array]"==Object.prototype.toString.call(r)};
|
||||
},{}],15:[function(require,module,exports){
|
||||
Cache.prototype.addAll||(Cache.prototype.addAll=function(t){function e(t){this.name="NetworkError",this.code=19,this.message=t}var r=this;return e.prototype=Object.create(Error.prototype),Promise.resolve().then(function(){if(arguments.length<1)throw new TypeError;return t=t.map(function(t){return t instanceof Request?t:String(t)}),Promise.all(t.map(function(t){"string"==typeof t&&(t=new Request(t));var r=new URL(t.url).protocol;if("http:"!==r&&"https:"!==r)throw new e("Invalid scheme");return fetch(t.clone())}))}).then(function(e){return Promise.all(e.map(function(e,n){return r.put(t[n],e)}))}).then(function(){})});
|
||||
},{}]},{},[12])(12)
|
||||
(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 < r.length; o++) s(r[o]);
|
||||
return s;
|
||||
})(
|
||||
{
|
||||
1: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function debug(e, n) {
|
||||
n = n || {};
|
||||
var t = n.debug || globalOptions.debug;
|
||||
t && console.log('[sw-toolbox] ' + e);
|
||||
}
|
||||
function openCache(e) {
|
||||
var n;
|
||||
return e && e.cache && (n = e.cache.name), (n =
|
||||
n || globalOptions.cache.name), caches.open(n);
|
||||
}
|
||||
function fetchAndCache(e, n) {
|
||||
n = n || {};
|
||||
var t = n.successResponses || globalOptions.successResponses;
|
||||
return fetch(e.clone()).then(function(c) {
|
||||
return 'GET' === e.method &&
|
||||
t.test(c.status) &&
|
||||
openCache(n).then(function(t) {
|
||||
t.put(e, c).then(function() {
|
||||
var c = n.cache || globalOptions.cache;
|
||||
(c.maxEntries || c.maxAgeSeconds) &&
|
||||
c.name &&
|
||||
queueCacheExpiration(e, t, c);
|
||||
});
|
||||
}), c.clone();
|
||||
});
|
||||
}
|
||||
function queueCacheExpiration(e, n, t) {
|
||||
var c = cleanupCache.bind(null, e, n, t);
|
||||
cleanupQueue = cleanupQueue ? cleanupQueue.then(c) : c();
|
||||
}
|
||||
function cleanupCache(e, n, t) {
|
||||
var c = e.url,
|
||||
a = t.maxAgeSeconds,
|
||||
u = t.maxEntries,
|
||||
o = t.name,
|
||||
r = Date.now();
|
||||
return debug(
|
||||
'Updating LRU order for ' +
|
||||
c +
|
||||
'. Max entries is ' +
|
||||
u +
|
||||
', max age is ' +
|
||||
a
|
||||
), idbCacheExpiration
|
||||
.getDb(o)
|
||||
.then(function(e) {
|
||||
return idbCacheExpiration.setTimestampForUrl(e, c, r);
|
||||
})
|
||||
.then(function(e) {
|
||||
return idbCacheExpiration.expireEntries(e, u, a, r);
|
||||
})
|
||||
.then(function(e) {
|
||||
debug('Successfully updated IDB.');
|
||||
var t = e.map(function(e) {
|
||||
return n['delete'](e);
|
||||
});
|
||||
return Promise.all(t).then(function() {
|
||||
debug('Done with cache cleanup.');
|
||||
});
|
||||
})['catch'](function(e) {
|
||||
debug(e);
|
||||
});
|
||||
}
|
||||
function renameCache(e, n, t) {
|
||||
return debug(
|
||||
'Renaming cache: [' + e + '] to [' + n + ']',
|
||||
t
|
||||
), caches['delete'](n).then(function() {
|
||||
return Promise.all([
|
||||
caches.open(e),
|
||||
caches.open(n),
|
||||
]).then(function(n) {
|
||||
var t = n[0], c = n[1];
|
||||
return t
|
||||
.keys()
|
||||
.then(function(e) {
|
||||
return Promise.all(
|
||||
e.map(function(e) {
|
||||
return t.match(e).then(function(n) {
|
||||
return c.put(e, n);
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(function() {
|
||||
return caches['delete'](e);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
var globalOptions = require('./options'),
|
||||
idbCacheExpiration = require('./idb-cache-expiration'),
|
||||
cleanupQueue;
|
||||
module.exports = {
|
||||
debug: debug,
|
||||
fetchAndCache: fetchAndCache,
|
||||
openCache: openCache,
|
||||
renameCache: renameCache,
|
||||
};
|
||||
},
|
||||
{ './idb-cache-expiration': 2, './options': 3 },
|
||||
],
|
||||
2: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function openDb(e) {
|
||||
return new Promise(function(r, n) {
|
||||
var t = indexedDB.open(DB_PREFIX + e, DB_VERSION);
|
||||
(t.onupgradeneeded = function() {
|
||||
var e = t.result.createObjectStore(STORE_NAME, {
|
||||
keyPath: URL_PROPERTY,
|
||||
});
|
||||
e.createIndex(TIMESTAMP_PROPERTY, TIMESTAMP_PROPERTY, {
|
||||
unique: !1,
|
||||
});
|
||||
}), (t.onsuccess = function() {
|
||||
r(t.result);
|
||||
}), (t.onerror = function() {
|
||||
n(t.error);
|
||||
});
|
||||
});
|
||||
}
|
||||
function getDb(e) {
|
||||
return e in cacheNameToDbPromise ||
|
||||
(cacheNameToDbPromise[e] = openDb(e)), cacheNameToDbPromise[e];
|
||||
}
|
||||
function setTimestampForUrl(e, r, n) {
|
||||
return new Promise(function(t, o) {
|
||||
var i = e.transaction(STORE_NAME, 'readwrite'),
|
||||
u = i.objectStore(STORE_NAME);
|
||||
u.put({ url: r, timestamp: n }), (i.oncomplete = function() {
|
||||
t(e);
|
||||
}), (i.onabort = function() {
|
||||
o(i.error);
|
||||
});
|
||||
});
|
||||
}
|
||||
function expireOldEntries(e, r, n) {
|
||||
return r
|
||||
? new Promise(function(t, o) {
|
||||
var i = 1e3 * r,
|
||||
u = [],
|
||||
c = e.transaction(STORE_NAME, 'readwrite'),
|
||||
s = c.objectStore(STORE_NAME),
|
||||
a = s.index(TIMESTAMP_PROPERTY);
|
||||
(a.openCursor().onsuccess = function(e) {
|
||||
var r = e.target.result;
|
||||
if (r && n - i > r.value[TIMESTAMP_PROPERTY]) {
|
||||
var t = r.value[URL_PROPERTY];
|
||||
u.push(t), s['delete'](t), r['continue']();
|
||||
}
|
||||
}), (c.oncomplete = function() {
|
||||
t(u);
|
||||
}), (c.onabort = o);
|
||||
})
|
||||
: Promise.resolve([]);
|
||||
}
|
||||
function expireExtraEntries(e, r) {
|
||||
return r
|
||||
? new Promise(function(n, t) {
|
||||
var o = [],
|
||||
i = e.transaction(STORE_NAME, 'readwrite'),
|
||||
u = i.objectStore(STORE_NAME),
|
||||
c = u.index(TIMESTAMP_PROPERTY),
|
||||
s = c.count();
|
||||
(c.count().onsuccess = function() {
|
||||
var e = s.result;
|
||||
e > r &&
|
||||
(c.openCursor().onsuccess = function(n) {
|
||||
var t = n.target.result;
|
||||
if (t) {
|
||||
var i = t.value[URL_PROPERTY];
|
||||
o.push(i), u['delete'](i), e - o.length > r &&
|
||||
t['continue']();
|
||||
}
|
||||
});
|
||||
}), (i.oncomplete = function() {
|
||||
n(o);
|
||||
}), (i.onabort = t);
|
||||
})
|
||||
: Promise.resolve([]);
|
||||
}
|
||||
function expireEntries(e, r, n, t) {
|
||||
return expireOldEntries(e, n, t).then(function(n) {
|
||||
return expireExtraEntries(e, r).then(function(e) {
|
||||
return n.concat(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
var DB_PREFIX = 'sw-toolbox-',
|
||||
DB_VERSION = 1,
|
||||
STORE_NAME = 'store',
|
||||
URL_PROPERTY = 'url',
|
||||
TIMESTAMP_PROPERTY = 'timestamp',
|
||||
cacheNameToDbPromise = {};
|
||||
module.exports = {
|
||||
getDb: getDb,
|
||||
setTimestampForUrl: setTimestampForUrl,
|
||||
expireEntries: expireEntries,
|
||||
};
|
||||
},
|
||||
{},
|
||||
],
|
||||
3: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
var scope;
|
||||
(scope = self.registration
|
||||
? self.registration.scope
|
||||
: self.scope ||
|
||||
new URL('./', self.location).href), (module.exports = {
|
||||
cache: {
|
||||
name: '$$$toolbox-cache$$$' + scope + '$$$',
|
||||
maxAgeSeconds: null,
|
||||
maxEntries: null,
|
||||
},
|
||||
debug: !1,
|
||||
networkTimeoutSeconds: null,
|
||||
preCacheItems: [],
|
||||
successResponses: /^0|([123]\d\d)|(40[14567])|410$/,
|
||||
});
|
||||
},
|
||||
{},
|
||||
],
|
||||
4: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
var url = new URL('./', self.location),
|
||||
basePath = url.pathname,
|
||||
pathRegexp = require('path-to-regexp'),
|
||||
Route = function(e, t, i, s) {
|
||||
t instanceof RegExp
|
||||
? (this.fullUrlRegExp = t)
|
||||
: (0 !== t.indexOf('/') && (t = basePath + t), (this.keys = [
|
||||
]), (this.regexp = pathRegexp(
|
||||
t,
|
||||
this.keys
|
||||
))), (this.method = e), (this.options = s), (this.handler = i);
|
||||
};
|
||||
(Route.prototype.makeHandler = function(e) {
|
||||
var t;
|
||||
if (this.regexp) {
|
||||
var i = this.regexp.exec(e);
|
||||
(t = {}), this.keys.forEach(function(e, s) {
|
||||
t[e.name] = i[s + 1];
|
||||
});
|
||||
}
|
||||
return function(e) {
|
||||
return this.handler(e, t, this.options);
|
||||
}.bind(this);
|
||||
}), (module.exports = Route);
|
||||
},
|
||||
{ 'path-to-regexp': 13 },
|
||||
],
|
||||
5: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function regexEscape(e) {
|
||||
return e.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
}
|
||||
var Route = require('./route'),
|
||||
keyMatch = function(e, t) {
|
||||
for (var r = e.entries(), o = r.next(); !o.done; ) {
|
||||
var n = new RegExp(o.value[0]);
|
||||
if (n.test(t)) return o.value[1];
|
||||
o = r.next();
|
||||
}
|
||||
return null;
|
||||
},
|
||||
Router = function() {
|
||||
(this.routes = new Map()), (this['default'] = null);
|
||||
};
|
||||
['get', 'post', 'put', 'delete', 'head', 'any'].forEach(function(e) {
|
||||
Router.prototype[e] = function(t, r, o) {
|
||||
return this.add(e, t, r, o);
|
||||
};
|
||||
}), (Router.prototype.add = function(e, t, r, o) {
|
||||
o = o || {};
|
||||
var n;
|
||||
t instanceof RegExp
|
||||
? (n = RegExp)
|
||||
: ((n = o.origin || self.location.origin), (n = n instanceof
|
||||
RegExp
|
||||
? n.source
|
||||
: regexEscape(n))), (e = e.toLowerCase());
|
||||
var u = new Route(e, t, r, o);
|
||||
this.routes.has(n) || this.routes.set(n, new Map());
|
||||
var a = this.routes.get(n);
|
||||
a.has(e) || a.set(e, new Map());
|
||||
var s = a.get(e), i = u.regexp || u.fullUrlRegExp;
|
||||
s.set(i.source, u);
|
||||
}), (Router.prototype.matchMethod = function(e, t) {
|
||||
var r = new URL(t), o = r.origin, n = r.pathname;
|
||||
return (
|
||||
this._match(e, keyMatch(this.routes, o), n) ||
|
||||
this._match(e, this.routes.get(RegExp), t)
|
||||
);
|
||||
}), (Router.prototype._match = function(e, t, r) {
|
||||
if (t) {
|
||||
var o = t.get(e.toLowerCase());
|
||||
if (o) {
|
||||
var n = keyMatch(o, r);
|
||||
if (n) return n.makeHandler(r);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}), (Router.prototype.match = function(e) {
|
||||
return (
|
||||
this.matchMethod(e.method, e.url) ||
|
||||
this.matchMethod('any', e.url)
|
||||
);
|
||||
}), (module.exports = new Router());
|
||||
},
|
||||
{ './route': 4 },
|
||||
],
|
||||
6: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function cacheFirst(e, r, t) {
|
||||
return helpers.debug(
|
||||
'Strategy: cache first [' + e.url + ']',
|
||||
t
|
||||
), helpers.openCache(t).then(function(r) {
|
||||
return r.match(e).then(function(r) {
|
||||
return r ? r : helpers.fetchAndCache(e, t);
|
||||
});
|
||||
});
|
||||
}
|
||||
var helpers = require('../helpers');
|
||||
module.exports = cacheFirst;
|
||||
},
|
||||
{ '../helpers': 1 },
|
||||
],
|
||||
7: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function cacheOnly(e, r, c) {
|
||||
return helpers.debug(
|
||||
'Strategy: cache only [' + e.url + ']',
|
||||
c
|
||||
), helpers.openCache(c).then(function(r) {
|
||||
return r.match(e);
|
||||
});
|
||||
}
|
||||
var helpers = require('../helpers');
|
||||
module.exports = cacheOnly;
|
||||
},
|
||||
{ '../helpers': 1 },
|
||||
],
|
||||
8: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function fastest(e, n, t) {
|
||||
return helpers.debug(
|
||||
'Strategy: fastest [' + e.url + ']',
|
||||
t
|
||||
), new Promise(function(r, s) {
|
||||
var c = !1,
|
||||
o = [],
|
||||
a = function(e) {
|
||||
o.push(e.toString()), c
|
||||
? s(
|
||||
new Error(
|
||||
'Both cache and network failed: "' +
|
||||
o.join('", "') +
|
||||
'"'
|
||||
)
|
||||
)
|
||||
: (c = !0);
|
||||
},
|
||||
h = function(e) {
|
||||
e instanceof Response ? r(e) : a('No result returned');
|
||||
};
|
||||
helpers
|
||||
.fetchAndCache(e.clone(), t)
|
||||
.then(h, a), cacheOnly(e, n, t).then(h, a);
|
||||
});
|
||||
}
|
||||
var helpers = require('../helpers'),
|
||||
cacheOnly = require('./cacheOnly');
|
||||
module.exports = fastest;
|
||||
},
|
||||
{ '../helpers': 1, './cacheOnly': 7 },
|
||||
],
|
||||
9: [
|
||||
function(require, module, exports) {
|
||||
module.exports = {
|
||||
networkOnly: require('./networkOnly'),
|
||||
networkFirst: require('./networkFirst'),
|
||||
cacheOnly: require('./cacheOnly'),
|
||||
cacheFirst: require('./cacheFirst'),
|
||||
fastest: require('./fastest'),
|
||||
};
|
||||
},
|
||||
{
|
||||
'./cacheFirst': 6,
|
||||
'./cacheOnly': 7,
|
||||
'./fastest': 8,
|
||||
'./networkFirst': 10,
|
||||
'./networkOnly': 11,
|
||||
},
|
||||
],
|
||||
10: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function networkFirst(e, r, t) {
|
||||
t = t || {};
|
||||
var s = t.successResponses || globalOptions.successResponses,
|
||||
n =
|
||||
t.networkTimeoutSeconds || globalOptions.networkTimeoutSeconds;
|
||||
return helpers.debug(
|
||||
'Strategy: network first [' + e.url + ']',
|
||||
t
|
||||
), helpers.openCache(t).then(function(r) {
|
||||
var o, u, c = [];
|
||||
if (n) {
|
||||
var i = new Promise(function(t) {
|
||||
o = setTimeout(function() {
|
||||
r.match(e).then(function(e) {
|
||||
e && t(e);
|
||||
});
|
||||
}, 1e3 * n);
|
||||
});
|
||||
c.push(i);
|
||||
}
|
||||
var a = helpers.fetchAndCache(e, t).then(function(e) {
|
||||
if ((o && clearTimeout(o), s.test(e.status))) return e;
|
||||
throw (helpers.debug(
|
||||
'Response was an HTTP error: ' + e.statusText,
|
||||
t
|
||||
), (u = e), new Error('Bad response'));
|
||||
})['catch'](function() {
|
||||
return helpers.debug(
|
||||
'Network or response error, fallback to cache [' +
|
||||
e.url +
|
||||
']',
|
||||
t
|
||||
), r.match(e).then(function(e) {
|
||||
return e || u;
|
||||
});
|
||||
});
|
||||
return c.push(a), Promise.race(c);
|
||||
});
|
||||
}
|
||||
var globalOptions = require('../options'),
|
||||
helpers = require('../helpers');
|
||||
module.exports = networkFirst;
|
||||
},
|
||||
{ '../helpers': 1, '../options': 3 },
|
||||
],
|
||||
11: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function networkOnly(e, r, t) {
|
||||
return helpers.debug(
|
||||
'Strategy: network only [' + e.url + ']',
|
||||
t
|
||||
), fetch(e);
|
||||
}
|
||||
var helpers = require('../helpers');
|
||||
module.exports = networkOnly;
|
||||
},
|
||||
{ '../helpers': 1 },
|
||||
],
|
||||
12: [
|
||||
function(require, module, exports) {
|
||||
'use strict';
|
||||
function cache(e, t) {
|
||||
return helpers.openCache(t).then(function(t) {
|
||||
return t.add(e);
|
||||
});
|
||||
}
|
||||
function uncache(e, t) {
|
||||
return helpers.openCache(t).then(function(t) {
|
||||
return t['delete'](e);
|
||||
});
|
||||
}
|
||||
function precache(e) {
|
||||
Array.isArray(e) ||
|
||||
(e = [e]), (options.preCacheItems = options.preCacheItems.concat(
|
||||
e
|
||||
));
|
||||
}
|
||||
require('serviceworker-cache-polyfill');
|
||||
var options = require('./options'),
|
||||
router = require('./router'),
|
||||
helpers = require('./helpers'),
|
||||
strategies = require('./strategies');
|
||||
helpers.debug('Service Worker Toolbox is loading');
|
||||
var flatten = function(e) {
|
||||
return e.reduce(function(e, t) {
|
||||
return e.concat(t);
|
||||
}, []);
|
||||
};
|
||||
self.addEventListener('install', function(e) {
|
||||
var t = options.cache.name + '$$$inactive$$$';
|
||||
helpers.debug(
|
||||
'install event fired'
|
||||
), helpers.debug('creating cache [' + t + ']'), e.waitUntil(
|
||||
helpers.openCache({ cache: { name: t } }).then(function(e) {
|
||||
return Promise.all(options.preCacheItems)
|
||||
.then(flatten)
|
||||
.then(function(t) {
|
||||
return helpers.debug(
|
||||
'preCache list: ' + (t.join(', ') || '(none)')
|
||||
), e.addAll(t);
|
||||
});
|
||||
})
|
||||
);
|
||||
}), self.addEventListener('activate', function(e) {
|
||||
helpers.debug('activate event fired');
|
||||
var t = options.cache.name + '$$$inactive$$$';
|
||||
e.waitUntil(helpers.renameCache(t, options.cache.name));
|
||||
}), self.addEventListener('fetch', function(e) {
|
||||
var t = router.match(e.request);
|
||||
t
|
||||
? e.respondWith(t(e.request))
|
||||
: router['default'] &&
|
||||
'GET' === e.request.method &&
|
||||
e.respondWith(router['default'](e.request));
|
||||
}), (module.exports = {
|
||||
networkOnly: strategies.networkOnly,
|
||||
networkFirst: strategies.networkFirst,
|
||||
cacheOnly: strategies.cacheOnly,
|
||||
cacheFirst: strategies.cacheFirst,
|
||||
fastest: strategies.fastest,
|
||||
router: router,
|
||||
options: options,
|
||||
cache: cache,
|
||||
uncache: uncache,
|
||||
precache: precache,
|
||||
});
|
||||
},
|
||||
{
|
||||
'./helpers': 1,
|
||||
'./options': 3,
|
||||
'./router': 5,
|
||||
'./strategies': 9,
|
||||
'serviceworker-cache-polyfill': 15,
|
||||
},
|
||||
],
|
||||
13: [
|
||||
function(require, module, exports) {
|
||||
function parse(e) {
|
||||
for (
|
||||
var t, r = [], n = 0, o = 0, p = '';
|
||||
null != (t = PATH_REGEXP.exec(e));
|
||||
|
||||
) {
|
||||
var a = t[0], i = t[1], s = t.index;
|
||||
if (((p += e.slice(o, s)), (o = s + a.length), i)) p += i[1];
|
||||
else {
|
||||
p && (r.push(p), (p = ''));
|
||||
var u = t[2],
|
||||
c = t[3],
|
||||
l = t[4],
|
||||
f = t[5],
|
||||
g = t[6],
|
||||
x = t[7],
|
||||
h = '+' === g || '*' === g,
|
||||
m = '?' === g || '*' === g,
|
||||
y = u || '/',
|
||||
T = l || f || (x ? '.*' : '[^' + y + ']+?');
|
||||
r.push({
|
||||
name: c || n++,
|
||||
prefix: u || '',
|
||||
delimiter: y,
|
||||
optional: m,
|
||||
repeat: h,
|
||||
pattern: escapeGroup(T),
|
||||
});
|
||||
}
|
||||
}
|
||||
return o < e.length && (p += e.substr(o)), p && r.push(p), r;
|
||||
}
|
||||
function compile(e) {
|
||||
return tokensToFunction(parse(e));
|
||||
}
|
||||
function tokensToFunction(e) {
|
||||
for (var t = new Array(e.length), r = 0; r < e.length; r++)
|
||||
'object' == typeof e[r] &&
|
||||
(t[r] = new RegExp('^' + e[r].pattern + '$'));
|
||||
return function(r) {
|
||||
for (var n = '', o = r || {}, p = 0; p < e.length; p++) {
|
||||
var a = e[p];
|
||||
if ('string' != typeof a) {
|
||||
var i, s = o[a.name];
|
||||
if (null == s) {
|
||||
if (a.optional) continue;
|
||||
throw new TypeError(
|
||||
'Expected "' + a.name + '" to be defined'
|
||||
);
|
||||
}
|
||||
if (isarray(s)) {
|
||||
if (!a.repeat)
|
||||
throw new TypeError(
|
||||
'Expected "' +
|
||||
a.name +
|
||||
'" to not repeat, but received "' +
|
||||
s +
|
||||
'"'
|
||||
);
|
||||
if (0 === s.length) {
|
||||
if (a.optional) continue;
|
||||
throw new TypeError(
|
||||
'Expected "' + a.name + '" to not be empty'
|
||||
);
|
||||
}
|
||||
for (var u = 0; u < s.length; u++) {
|
||||
if (((i = encodeURIComponent(s[u])), !t[p].test(i)))
|
||||
throw new TypeError(
|
||||
'Expected all "' +
|
||||
a.name +
|
||||
'" to match "' +
|
||||
a.pattern +
|
||||
'", but received "' +
|
||||
i +
|
||||
'"'
|
||||
);
|
||||
n += (0 === u ? a.prefix : a.delimiter) + i;
|
||||
}
|
||||
} else {
|
||||
if (((i = encodeURIComponent(s)), !t[p].test(i)))
|
||||
throw new TypeError(
|
||||
'Expected "' +
|
||||
a.name +
|
||||
'" to match "' +
|
||||
a.pattern +
|
||||
'", but received "' +
|
||||
i +
|
||||
'"'
|
||||
);
|
||||
n += a.prefix + i;
|
||||
}
|
||||
} else n += a;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
}
|
||||
function escapeString(e) {
|
||||
return e.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1');
|
||||
}
|
||||
function escapeGroup(e) {
|
||||
return e.replace(/([=!:$\/()])/g, '\\$1');
|
||||
}
|
||||
function attachKeys(e, t) {
|
||||
return (e.keys = t), e;
|
||||
}
|
||||
function flags(e) {
|
||||
return e.sensitive ? '' : 'i';
|
||||
}
|
||||
function regexpToRegexp(e, t) {
|
||||
var r = e.source.match(/\((?!\?)/g);
|
||||
if (r)
|
||||
for (var n = 0; n < r.length; n++)
|
||||
t.push({
|
||||
name: n,
|
||||
prefix: null,
|
||||
delimiter: null,
|
||||
optional: !1,
|
||||
repeat: !1,
|
||||
pattern: null,
|
||||
});
|
||||
return attachKeys(e, t);
|
||||
}
|
||||
function arrayToRegexp(e, t, r) {
|
||||
for (var n = [], o = 0; o < e.length; o++)
|
||||
n.push(pathToRegexp(e[o], t, r).source);
|
||||
var p = new RegExp('(?:' + n.join('|') + ')', flags(r));
|
||||
return attachKeys(p, t);
|
||||
}
|
||||
function stringToRegexp(e, t, r) {
|
||||
for (
|
||||
var n = parse(e), o = tokensToRegExp(n, r), p = 0;
|
||||
p < n.length;
|
||||
p++
|
||||
)
|
||||
'string' != typeof n[p] && t.push(n[p]);
|
||||
return attachKeys(o, t);
|
||||
}
|
||||
function tokensToRegExp(e, t) {
|
||||
t = t || {};
|
||||
for (
|
||||
var r = t.strict,
|
||||
n = t.end !== !1,
|
||||
o = '',
|
||||
p = e[e.length - 1],
|
||||
a = 'string' == typeof p && /\/$/.test(p),
|
||||
i = 0;
|
||||
i < e.length;
|
||||
i++
|
||||
) {
|
||||
var s = e[i];
|
||||
if ('string' == typeof s) o += escapeString(s);
|
||||
else {
|
||||
var u = escapeString(s.prefix), c = s.pattern;
|
||||
s.repeat && (c += '(?:' + u + c + ')*'), (c = s.optional
|
||||
? u ? '(?:' + u + '(' + c + '))?' : '(' + c + ')?'
|
||||
: u + '(' + c + ')'), (o += c);
|
||||
}
|
||||
}
|
||||
return r ||
|
||||
(o = (a ? o.slice(0, -2) : o) + '(?:\\/(?=$))?'), (o += n
|
||||
? '$'
|
||||
: r && a ? '' : '(?=\\/|$)'), new RegExp('^' + o, flags(t));
|
||||
}
|
||||
function pathToRegexp(e, t, r) {
|
||||
return (t = t || []), isarray(t)
|
||||
? r || (r = {})
|
||||
: ((r = t), (t = [])), e instanceof RegExp
|
||||
? regexpToRegexp(e, t, r)
|
||||
: isarray(e) ? arrayToRegexp(e, t, r) : stringToRegexp(e, t, r);
|
||||
}
|
||||
var isarray = require('isarray');
|
||||
(module.exports = pathToRegexp), (module.exports.parse = parse), (module.exports.compile = compile), (module.exports.tokensToFunction = tokensToFunction), (module.exports.tokensToRegExp = tokensToRegExp);
|
||||
var PATH_REGEXP = new RegExp(
|
||||
[
|
||||
'(\\\\.)',
|
||||
'([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))',
|
||||
].join('|'),
|
||||
'g'
|
||||
);
|
||||
},
|
||||
{ isarray: 14 },
|
||||
],
|
||||
14: [
|
||||
function(require, module, exports) {
|
||||
module.exports =
|
||||
Array.isArray ||
|
||||
function(r) {
|
||||
return '[object Array]' == Object.prototype.toString.call(r);
|
||||
};
|
||||
},
|
||||
{},
|
||||
],
|
||||
15: [
|
||||
function(require, module, exports) {
|
||||
Cache.prototype.addAll ||
|
||||
(Cache.prototype.addAll = function(t) {
|
||||
function e(t) {
|
||||
(this.name =
|
||||
'NetworkError'), (this.code = 19), (this.message = t);
|
||||
}
|
||||
var r = this;
|
||||
return (e.prototype = Object.create(
|
||||
Error.prototype
|
||||
)), Promise.resolve()
|
||||
.then(function() {
|
||||
if (arguments.length < 1) throw new TypeError();
|
||||
return (t = t.map(function(t) {
|
||||
return t instanceof Request ? t : String(t);
|
||||
})), Promise.all(
|
||||
t.map(function(t) {
|
||||
'string' == typeof t && (t = new Request(t));
|
||||
var r = new URL(t.url).protocol;
|
||||
if ('http:' !== r && 'https:' !== r)
|
||||
throw new e('Invalid scheme');
|
||||
return fetch(t.clone());
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(function(e) {
|
||||
return Promise.all(
|
||||
e.map(function(e, n) {
|
||||
return r.put(t[n], e);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(function() {});
|
||||
});
|
||||
},
|
||||
{},
|
||||
],
|
||||
},
|
||||
{},
|
||||
[12]
|
||||
)(12);
|
||||
});
|
||||
|
||||
(global => {
|
||||
'use strict';
|
||||
|
||||
// Assets
|
||||
global.toolbox.router.get(/\/static\//, global.toolbox.cacheFirst, { cache: { name: 'static' } });
|
||||
global.toolbox.router.get('/(.*)', global.toolbox.fastest, {origin: 'https://secure.gravatar.com'});
|
||||
global.toolbox.router.get(/\/static\//, global.toolbox.cacheFirst, {
|
||||
cache: { name: 'static' },
|
||||
});
|
||||
global.toolbox.router.get('/(.*)', global.toolbox.fastest, {
|
||||
origin: 'https://secure.gravatar.com',
|
||||
});
|
||||
|
||||
// API
|
||||
global.toolbox.router.get(/\/api\//, global.toolbox.networkFirst, {
|
||||
cache: {
|
||||
name: 'api',
|
||||
maxEntries: 100
|
||||
}
|
||||
cache: {
|
||||
name: 'api',
|
||||
maxEntries: 100,
|
||||
},
|
||||
});
|
||||
|
||||
// API GET calls
|
||||
|
@ -69,8 +873,10 @@ Cache.prototype.addAll||(Cache.prototype.addAll=function(t){function e(t){this.n
|
|||
|
||||
// Boilerplate to ensure our service worker takes control of the page as soon
|
||||
// as possible.
|
||||
global.addEventListener('install',
|
||||
event => event.waitUntil(global.skipWaiting()));
|
||||
global.addEventListener('activate',
|
||||
event => event.waitUntil(global.clients.claim()));
|
||||
global.addEventListener('install', event =>
|
||||
event.waitUntil(global.skipWaiting())
|
||||
);
|
||||
global.addEventListener('activate', event =>
|
||||
event.waitUntil(global.clients.claim())
|
||||
);
|
||||
})(self);
|
||||
|
|
|
@ -21,8 +21,7 @@ function runMigrations() {
|
|||
path: './server/migrations',
|
||||
},
|
||||
});
|
||||
return umzug.up()
|
||||
.then(() => {
|
||||
return umzug.up().then(() => {
|
||||
return sequelize.close();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@ import { sequelize } from '../sequelize';
|
|||
|
||||
export function flushdb() {
|
||||
const sql = sequelize.getQueryInterface();
|
||||
const tables = Object.keys(sequelize.models).map((model) =>
|
||||
sql.quoteTable(sequelize.models[model].getTableName()));
|
||||
const tables = Object.keys(sequelize.models).map(model =>
|
||||
sql.quoteTable(sequelize.models[model].getTableName())
|
||||
);
|
||||
const query = `TRUNCATE ${tables.join(', ')} CASCADE`;
|
||||
|
||||
return sequelize.query(query);
|
||||
|
@ -24,7 +25,4 @@ const seed = async () => {
|
|||
});
|
||||
};
|
||||
|
||||
export {
|
||||
seed,
|
||||
sequelize,
|
||||
};
|
||||
export { seed, sequelize };
|
||||
|
|
|
@ -4,29 +4,28 @@ import moment from 'moment';
|
|||
const makePolicy = () => {
|
||||
const policy = {
|
||||
conditions: [
|
||||
{'bucket': process.env.AWS_S3_UPLOAD_BUCKET_NAME},
|
||||
{ bucket: process.env.AWS_S3_UPLOAD_BUCKET_NAME },
|
||||
['starts-with', '$key', ''],
|
||||
{'acl': 'public-read'},
|
||||
{ acl: 'public-read' },
|
||||
['content-length-range', 0, process.env.AWS_S3_UPLOAD_MAX_SIZE],
|
||||
['starts-with', '$Content-Type', 'image'],
|
||||
['starts-with', '$Cache-Control', ''],
|
||||
],
|
||||
expiration: moment().add(24*60, 'minutes').format('YYYY-MM-DDTHH:mm:ss\\Z'),
|
||||
expiration: moment()
|
||||
.add(24 * 60, 'minutes')
|
||||
.format('YYYY-MM-DDTHH:mm:ss\\Z'),
|
||||
};
|
||||
|
||||
return new Buffer(JSON.stringify(policy)).toString('base64')
|
||||
return new Buffer(JSON.stringify(policy)).toString('base64');
|
||||
};
|
||||
|
||||
const signPolicy = (policy) => {
|
||||
const signature = crypto.createHmac(
|
||||
'sha1',
|
||||
process.env.AWS_SECRET_ACCESS_KEY
|
||||
).update(policy).digest('base64');
|
||||
const signPolicy = policy => {
|
||||
const signature = crypto
|
||||
.createHmac('sha1', process.env.AWS_SECRET_ACCESS_KEY)
|
||||
.update(policy)
|
||||
.digest('base64');
|
||||
|
||||
return signature;
|
||||
};
|
||||
|
||||
export {
|
||||
makePolicy,
|
||||
signPolicy,
|
||||
};
|
||||
export { makePolicy, signPolicy };
|
||||
|
|
|
@ -5,7 +5,7 @@ truncate.defaultOptions = {
|
|||
stripTags: false,
|
||||
ellipsis: '...',
|
||||
decodeEntities: false,
|
||||
excludes: ['h1', 'pre', ],
|
||||
excludes: ['h1', 'pre'],
|
||||
};
|
||||
|
||||
const truncateMarkdown = (text, length) => {
|
||||
|
@ -13,6 +13,4 @@ const truncateMarkdown = (text, length) => {
|
|||
return truncate(html, length);
|
||||
};
|
||||
|
||||
export {
|
||||
truncateMarkdown,
|
||||
};
|
||||
export { truncateMarkdown };
|
||||
|
|
339
yarn.lock
339
yarn.lock
|
@ -79,7 +79,7 @@ amdefine@>=0.0.4:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33"
|
||||
|
||||
ansi-escapes@^1.1.0, ansi-escapes@^1.4.0:
|
||||
ansi-escapes@^1.0.0, ansi-escapes@^1.1.0, ansi-escapes@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
|
||||
|
||||
|
@ -95,6 +95,12 @@ ansi-styles@^2.2.1:
|
|||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||
|
||||
ansi-styles@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.0.0.tgz#5404e93a544c4fec7f048262977bebfe3155e0c1"
|
||||
dependencies:
|
||||
color-convert "^1.0.0"
|
||||
|
||||
ansicolors@~0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef"
|
||||
|
@ -114,6 +120,10 @@ ap@~0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ap/-/ap-0.2.0.tgz#ae0942600b29912f0d2b14ec60c45e8f330b6110"
|
||||
|
||||
app-root-path@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46"
|
||||
|
||||
append-transform@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.3.0.tgz#d6933ce4a85f09445d9ccc4cc119051b7381a813"
|
||||
|
@ -220,6 +230,10 @@ assert@^1.1.1:
|
|||
dependencies:
|
||||
util "0.10.3"
|
||||
|
||||
ast-types@0.9.8:
|
||||
version "0.9.8"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.8.tgz#6cb6a40beba31f49f20928e28439fc14a3dab078"
|
||||
|
||||
async-each@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
|
||||
|
@ -267,7 +281,15 @@ aws4@^1.2.1:
|
|||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755"
|
||||
|
||||
babel-code-frame@^6.16.0, babel-code-frame@^6.8.0:
|
||||
babel-code-frame@6.22.0, babel-code-frame@^6.16.0:
|
||||
version "6.22.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
|
||||
dependencies:
|
||||
chalk "^1.1.0"
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.0"
|
||||
|
||||
babel-code-frame@^6.8.0:
|
||||
version "6.16.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de"
|
||||
dependencies:
|
||||
|
@ -1043,6 +1065,10 @@ babel-types@^6.0.19, babel-types@^6.10.2, babel-types@^6.13.0, babel-types@^6.14
|
|||
lodash "^4.2.0"
|
||||
to-fast-properties "^1.0.1"
|
||||
|
||||
babylon@7.0.0-beta.8:
|
||||
version "7.0.0-beta.8"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.8.tgz#2bdc5ae366041442c27e068cce6f0d7c06ea9949"
|
||||
|
||||
babylon@^6.0.18, babylon@^6.11.0, babylon@^6.7.0, babylon@^6.8.1:
|
||||
version "6.11.4"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.11.4.tgz#75e1f52187efa0cde5a541a7f7fdda38f6eb5bd2"
|
||||
|
@ -1389,7 +1415,7 @@ center-align@^0.1.1:
|
|||
align-text "^0.1.3"
|
||||
lazy-cache "^1.0.3"
|
||||
|
||||
chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
dependencies:
|
||||
|
@ -1506,18 +1532,29 @@ cli-color@~0.3.2:
|
|||
memoizee "~0.3.8"
|
||||
timers-ext "0.1"
|
||||
|
||||
cli-cursor@^1.0.1:
|
||||
cli-cursor@^1.0.1, cli-cursor@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
|
||||
dependencies:
|
||||
restore-cursor "^1.0.1"
|
||||
|
||||
cli-spinners@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c"
|
||||
|
||||
cli-table@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
|
||||
dependencies:
|
||||
colors "1.0.3"
|
||||
|
||||
cli-truncate@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
|
||||
dependencies:
|
||||
slice-ansi "0.0.4"
|
||||
string-width "^1.0.1"
|
||||
|
||||
cli-usage@^0.1.1:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/cli-usage/-/cli-usage-0.1.4.tgz#7c01e0dc706c234b39c933838c8e20b2175776e2"
|
||||
|
@ -1585,7 +1622,7 @@ codemirror@5.16.0, codemirror@^5.13.4:
|
|||
version "5.16.0"
|
||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.16.0.tgz#468031dc9bda1b52e041f0482e5aa7f2b4e79cef"
|
||||
|
||||
color-convert@^1.3.0:
|
||||
color-convert@^1.0.0, color-convert@^1.3.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.5.0.tgz#7a2b4efb4488df85bca6443cb038b7100fbe7de1"
|
||||
|
||||
|
@ -1841,6 +1878,19 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
|
||||
cosmiconfig@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-1.1.0.tgz#0dea0f9804efdfb929fbb1b188e25553ea053d37"
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
js-yaml "^3.4.3"
|
||||
minimist "^1.2.0"
|
||||
object-assign "^4.0.1"
|
||||
os-homedir "^1.0.1"
|
||||
parse-json "^2.2.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
require-from-string "^1.1.0"
|
||||
|
||||
crc@3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/crc/-/crc-3.3.0.tgz#fa622e1bc388bf257309082d6b65200ce67090ba"
|
||||
|
@ -1889,6 +1939,14 @@ cross-spawn@^3.0.0:
|
|||
lru-cache "^4.0.1"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^5.0.1:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||
dependencies:
|
||||
lru-cache "^4.0.1"
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cryptiles@2.x.x:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
|
||||
|
@ -2068,6 +2126,10 @@ dashify@^0.2.0:
|
|||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/dashify/-/dashify-0.2.2.tgz#6a07415a01c91faf4a32e38d9dfba71f61cb20fe"
|
||||
|
||||
date-fns@^1.27.2:
|
||||
version "1.28.4"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.4.tgz#7938aec34ba31fc8bd134d2344bc2e0bbfd95165"
|
||||
|
||||
date-now@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/date-now/-/date-now-1.0.1.tgz#bb7d086438debe4182a485fb3df3fbfb99d6153c"
|
||||
|
@ -2341,6 +2403,10 @@ ee-first@1.1.1:
|
|||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
|
||||
elegant-spinner@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
|
||||
|
||||
elliptic@^6.0.0:
|
||||
version "6.3.2"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.2.tgz#e4c81e0829cf0a65ab70e998b8232723b5c1bc48"
|
||||
|
@ -2579,6 +2645,12 @@ eslint-config-airbnb@9.0.1:
|
|||
dependencies:
|
||||
eslint-config-airbnb-base "^3.0.0"
|
||||
|
||||
eslint-config-prettier@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-1.7.0.tgz#cda3ce22df1e852daa9370f1f3446e8b8a02ce44"
|
||||
dependencies:
|
||||
get-stdin "^5.0.1"
|
||||
|
||||
eslint-import-resolver-node@^0.2.0:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c"
|
||||
|
@ -2629,6 +2701,12 @@ eslint-plugin-jsx-a11y@^1.5.3:
|
|||
jsx-ast-utils "^1.0.0"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
eslint-plugin-prettier@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.0.1.tgz#2ae1216cf053dd728360ca8560bf1aabc8af3fa9"
|
||||
dependencies:
|
||||
requireindex "~1.1.0"
|
||||
|
||||
eslint-plugin-react@5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-5.2.2.tgz#7db068e1f5487f6871e4deef36a381c303eac161"
|
||||
|
@ -2704,7 +2782,7 @@ estraverse@~4.1.0:
|
|||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2"
|
||||
|
||||
esutils@^2.0.0, esutils@^2.0.2:
|
||||
esutils@2.0.2, esutils@^2.0.0, esutils@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||
|
||||
|
@ -2747,6 +2825,18 @@ exec-sh@^0.2.0:
|
|||
dependencies:
|
||||
merge "^1.1.3"
|
||||
|
||||
execa@^0.6.0:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-0.6.3.tgz#57b69a594f081759c69e5370f0d17b9cb11658fe"
|
||||
dependencies:
|
||||
cross-spawn "^5.0.1"
|
||||
get-stream "^3.0.0"
|
||||
is-stream "^1.1.0"
|
||||
npm-run-path "^2.0.0"
|
||||
p-finally "^1.0.0"
|
||||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
exit-hook@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
||||
|
@ -2860,7 +2950,7 @@ fetch-test-server@^1.1.0:
|
|||
debug "^2.2.0"
|
||||
node-fetch "^1.5.1"
|
||||
|
||||
figures@^1.3.5:
|
||||
figures@^1.3.5, figures@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||
dependencies:
|
||||
|
@ -2982,6 +3072,10 @@ flatten@1.0.2, flatten@^1.0.2:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
|
||||
|
||||
flow-parser@0.43.0:
|
||||
version "0.43.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.43.0.tgz#e2b8eb1ac83dd53f7b6b04a7c35b6a52c33479b7"
|
||||
|
||||
for-in@^0.1.5:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
|
||||
|
@ -3112,10 +3206,18 @@ get-caller-file@^1.0.1:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
|
||||
|
||||
get-stdin@5.0.1, get-stdin@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
|
||||
|
||||
get-stdin@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
||||
|
||||
get-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||
|
||||
getpass@^0.1.1:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6"
|
||||
|
@ -3168,16 +3270,7 @@ glob@5.x, glob@^5.0.15:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^4.3.1:
|
||||
version "4.5.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f"
|
||||
dependencies:
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^2.0.1"
|
||||
once "^1.3.0"
|
||||
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6:
|
||||
glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
|
||||
dependencies:
|
||||
|
@ -3188,6 +3281,15 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6:
|
|||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^4.3.1:
|
||||
version "4.5.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f"
|
||||
dependencies:
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^2.0.1"
|
||||
once "^1.3.0"
|
||||
|
||||
glob@~3.1.21:
|
||||
version "3.1.21"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd"
|
||||
|
@ -3703,6 +3805,10 @@ indent-string@^2.1.0:
|
|||
dependencies:
|
||||
repeating "^2.0.0"
|
||||
|
||||
indent-string@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.1.0.tgz#08ff4334603388399b329e6b9538dc7a3cf5de7d"
|
||||
|
||||
indexes-of@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
|
||||
|
@ -3910,6 +4016,10 @@ is-primitive@^2.0.0:
|
|||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
|
||||
|
||||
is-promise@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
|
||||
|
||||
is-property@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
|
||||
|
@ -3934,7 +4044,7 @@ is-resolvable@^1.0.0:
|
|||
dependencies:
|
||||
tryit "^1.0.1"
|
||||
|
||||
is-stream@^1.0.0, is-stream@^1.0.1:
|
||||
is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
|
||||
|
@ -4209,6 +4319,13 @@ jest-matcher-utils@^15.1.0:
|
|||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
|
||||
jest-matcher-utils@^19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-19.0.0.tgz#5ecd9b63565d2b001f61fbf7ec4c7f537964564d"
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
pretty-format "^19.0.0"
|
||||
|
||||
jest-matchers@^15.1.1:
|
||||
version "15.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-15.1.1.tgz#faff50acbbf9743323ec2270a24743cb59d638f0"
|
||||
|
@ -4277,6 +4394,15 @@ jest-util@^15.1.1:
|
|||
jest-mock "^15.0.0"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
jest-validate@19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-19.0.0.tgz#8c6318a20ecfeaba0ba5378bfbb8277abded4173"
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
jest-matcher-utils "^19.0.0"
|
||||
leven "^2.0.0"
|
||||
pretty-format "^19.0.0"
|
||||
|
||||
jodid25519@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967"
|
||||
|
@ -4313,11 +4439,15 @@ js-tokens@^2.0.0:
|
|||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5"
|
||||
|
||||
js-tokens@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
|
||||
|
||||
js-tree@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tree/-/js-tree-1.1.0.tgz#087ee3ec366a5b74eb14f486016c5e0e631f1670"
|
||||
|
||||
js-yaml@3.x, js-yaml@^3.5.1, js-yaml@~3.6.1:
|
||||
js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@~3.6.1:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30"
|
||||
dependencies:
|
||||
|
@ -4627,6 +4757,10 @@ lcid@^1.0.0:
|
|||
dependencies:
|
||||
invert-kv "^1.0.0"
|
||||
|
||||
leven@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
|
||||
|
||||
levn@^0.3.0, levn@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||
|
@ -4648,6 +4782,64 @@ liftoff@^2.1.0:
|
|||
rechoir "^0.6.2"
|
||||
resolve "^1.1.7"
|
||||
|
||||
lint-staged@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-3.4.0.tgz#52fa85dfc92bb1c6fe8ad0d0d98ca13924e03e4b"
|
||||
dependencies:
|
||||
app-root-path "^2.0.0"
|
||||
cosmiconfig "^1.1.0"
|
||||
execa "^0.6.0"
|
||||
listr "^0.11.0"
|
||||
minimatch "^3.0.0"
|
||||
npm-which "^3.0.1"
|
||||
staged-git-files "0.0.4"
|
||||
|
||||
listr-silent-renderer@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
|
||||
|
||||
listr-update-renderer@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9"
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
cli-truncate "^0.2.1"
|
||||
elegant-spinner "^1.0.1"
|
||||
figures "^1.7.0"
|
||||
indent-string "^3.0.0"
|
||||
log-symbols "^1.0.2"
|
||||
log-update "^1.0.2"
|
||||
strip-ansi "^3.0.1"
|
||||
|
||||
listr-verbose-renderer@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.0.tgz#44dc01bb0c34a03c572154d4d08cde9b1dc5620f"
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
cli-cursor "^1.0.2"
|
||||
date-fns "^1.27.2"
|
||||
figures "^1.7.0"
|
||||
|
||||
listr@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/listr/-/listr-0.11.0.tgz#5e778bc23806ac3ab984ed75564458151f39b03e"
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
cli-truncate "^0.2.1"
|
||||
figures "^1.7.0"
|
||||
indent-string "^2.1.0"
|
||||
is-promise "^2.1.0"
|
||||
is-stream "^1.1.0"
|
||||
listr-silent-renderer "^1.1.1"
|
||||
listr-update-renderer "^0.2.0"
|
||||
listr-verbose-renderer "^0.4.0"
|
||||
log-symbols "^1.0.2"
|
||||
log-update "^1.0.2"
|
||||
ora "^0.2.3"
|
||||
rxjs "^5.0.0-beta.11"
|
||||
stream-to-observable "^0.1.0"
|
||||
strip-ansi "^3.0.1"
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
|
@ -5051,6 +5243,19 @@ lodash@~4.9.0:
|
|||
version "4.9.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.9.0.tgz#4c20d742f03ce85dc700e0dd7ab9bcab85e6fc14"
|
||||
|
||||
log-symbols@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
|
||||
dependencies:
|
||||
chalk "^1.0.0"
|
||||
|
||||
log-update@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1"
|
||||
dependencies:
|
||||
ansi-escapes "^1.0.0"
|
||||
cli-cursor "^1.0.2"
|
||||
|
||||
longest@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
|
@ -5303,7 +5508,7 @@ minimist@0.0.8, minimist@~0.0.1:
|
|||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
|
||||
minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
|
||||
minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
|
||||
|
@ -5646,6 +5851,26 @@ normalizr@2.0.1:
|
|||
dependencies:
|
||||
lodash "^4.0.0"
|
||||
|
||||
npm-path@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.3.tgz#15cff4e1c89a38da77f56f6055b24f975dfb2bbe"
|
||||
dependencies:
|
||||
which "^1.2.10"
|
||||
|
||||
npm-run-path@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
|
||||
dependencies:
|
||||
path-key "^2.0.0"
|
||||
|
||||
npm-which@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa"
|
||||
dependencies:
|
||||
commander "^2.9.0"
|
||||
npm-path "^2.0.2"
|
||||
which "^1.2.10"
|
||||
|
||||
"npmlog@0 || 1 || 2 || 3":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-3.1.2.tgz#2d46fa874337af9498a2f12bb43d8d0be4a36873"
|
||||
|
@ -5786,6 +6011,15 @@ optionator@^0.8.1:
|
|||
type-check "~0.3.2"
|
||||
wordwrap "~1.0.0"
|
||||
|
||||
ora@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4"
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
cli-cursor "^1.0.2"
|
||||
cli-spinners "^0.1.2"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
orchestrator@^0.3.0:
|
||||
version "0.3.7"
|
||||
resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.7.tgz#c45064e22c5a2a7b99734f409a95ffedc7d3c3df"
|
||||
|
@ -5827,6 +6061,10 @@ osenv@0, osenv@^0.1.0, osenv@^0.1.3:
|
|||
os-homedir "^1.0.0"
|
||||
os-tmpdir "^1.0.0"
|
||||
|
||||
p-finally@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
|
||||
package-json@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0"
|
||||
|
@ -5934,6 +6172,10 @@ path-is-inside@^1.0.1:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||
|
||||
path-key@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||
|
||||
path-parse@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
|
||||
|
@ -6324,6 +6566,21 @@ preserve@^0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prettier@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.2.2.tgz#22d17c1132faaaea1f1d4faea31f19f7a1959f3e"
|
||||
dependencies:
|
||||
ast-types "0.9.8"
|
||||
babel-code-frame "6.22.0"
|
||||
babylon "7.0.0-beta.8"
|
||||
chalk "1.1.3"
|
||||
esutils "2.0.2"
|
||||
flow-parser "0.43.0"
|
||||
get-stdin "5.0.1"
|
||||
glob "7.1.1"
|
||||
jest-validate "19.0.0"
|
||||
minimist "1.2.0"
|
||||
|
||||
pretty-error@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.0.1.tgz#8a7375b9fe26e43b5101794e4dbeac362a8d629a"
|
||||
|
@ -6331,6 +6588,12 @@ pretty-error@^2.0.0:
|
|||
renderkid "~2.0.0"
|
||||
utila "~0.4"
|
||||
|
||||
pretty-format@^19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-19.0.0.tgz#56530d32acb98a3fa4851c4e2b9d37b420684c84"
|
||||
dependencies:
|
||||
ansi-styles "^3.0.0"
|
||||
|
||||
pretty-format@^3.7.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385"
|
||||
|
@ -6856,6 +7119,10 @@ require-directory@^2.1.1:
|
|||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
|
||||
require-from-string@^1.1.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
|
||||
|
||||
require-main-filename@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
|
||||
|
@ -6867,6 +7134,10 @@ require-uncached@^1.0.2:
|
|||
caller-path "^0.1.0"
|
||||
resolve-from "^1.0.0"
|
||||
|
||||
requireindex@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162"
|
||||
|
||||
resolve-dir@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e"
|
||||
|
@ -6947,6 +7218,12 @@ rx-lite@^3.1.2:
|
|||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
||||
|
||||
rxjs@^5.0.0-beta.11:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.3.0.tgz#d88ccbdd46af290cbdb97d5d8055e52453fabe2d"
|
||||
dependencies:
|
||||
symbol-observable "^1.0.1"
|
||||
|
||||
safestart@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/safestart/-/safestart-0.8.0.tgz#f6716cb863afa54db7fb2169c29ce85e30b5654d"
|
||||
|
@ -7134,6 +7411,12 @@ shallowequal@0.2.2:
|
|||
dependencies:
|
||||
lodash.keys "^3.1.2"
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
dependencies:
|
||||
shebang-regex "^1.0.0"
|
||||
|
||||
shebang-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
|
@ -7283,6 +7566,10 @@ stackframe@^0.3.1:
|
|||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4"
|
||||
|
||||
staged-git-files@0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35"
|
||||
|
||||
statuses@1, "statuses@>= 1.2.1 < 2", statuses@^1.0.0, statuses@^1.2.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a"
|
||||
|
@ -7325,6 +7612,10 @@ stream-shift@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
|
||||
|
||||
stream-to-observable@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe"
|
||||
|
||||
strict-uri-encode@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||
|
@ -7378,6 +7669,10 @@ strip-bom@^2.0.0:
|
|||
dependencies:
|
||||
is-utf8 "^0.2.0"
|
||||
|
||||
strip-eof@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
||||
|
||||
strip-indent@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
|
||||
|
@ -7423,6 +7718,10 @@ swap-case@^1.1.0:
|
|||
lower-case "^1.1.1"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
symbol-observable@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
|
||||
|
||||
"symbol-tree@>= 3.1.0 < 4.0.0":
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.1.4.tgz#02b279348d337debc39694c5c95f882d448a312a"
|
||||
|
|
Reference in New Issue