New database with migrations

This commit is contained in:
Jori Lallo 2016-06-20 00:18:03 -07:00
parent f2732aacab
commit 24e02bfdc4
12 changed files with 361 additions and 49 deletions

10
.sequelizerc Normal file
View File

@ -0,0 +1,10 @@
require('localenv');
var path = require('path');
module.exports = {
'config': path.resolve('server/config', 'database.json'),
'migrations-path': path.resolve('server', 'migrations'),
'models-path': path.resolve('server', 'models'),
'seeders-path': path.resolve('server/models', 'fixtures'),
}

View File

@ -11,7 +11,8 @@
"start": "cross-env NODE_ENV=development DEBUG=1 ./node_modules/.bin/nodemon --watch server index.js",
"lint": "eslint src",
"deploy": "git push heroku master",
"heroku-postbuild": "npm run build"
"heroku-postbuild": "npm run build && npm run sequelize db:migrate",
"sequelize": "./node_modules/.bin/sequelize"
},
"repository": {
"type": "git",
@ -97,7 +98,7 @@
"safestart": "^0.8.0",
"sass-loader": "^3.2.0",
"sequelize": "^3.21.0",
"sequelize-cli": "^2.3.1",
"sequelize-cli": "^2.4.0",
"sequelize-encrypted": "^0.1.0",
"slug": "^0.9.1",
"style-loader": "^0.13.0",

View File

@ -13,11 +13,11 @@ router.post('atlases.info', auth(), async (ctx) => {
let { id } = ctx.request.body;
ctx.assertPresent(id, 'id is required');
const team = await ctx.state.user.getTeam();
const user = ctx.state.user;
const atlas = await Atlas.findOne({
where: {
id: id,
teamId: team.id,
teamId: user.teamId,
},
});
@ -30,10 +30,10 @@ router.post('atlases.info', auth(), async (ctx) => {
router.post('atlases.list', auth(), pagination(), async (ctx) => {
const team = await ctx.state.user.getTeam();
const user = ctx.state.user;
const atlases = await Atlas.findAll({
where: {
teamId: team.id,
teamId: user.teamId,
},
order: [
['updatedAt', 'DESC'],

View File

@ -40,13 +40,27 @@ router.post('auth.slack', async (ctx) => {
const authResponse = await fetch(`https://slack.com/api/auth.test?token=${data.access_token}`);
const authData = await authResponse.json();
// Team
let team = await Team.findOne({ where: { slackId: data.team.id } });
if (!team) {
team = await Team.create({
name: data.team.name,
slackId: data.team.id,
slackData: data.team,
});
const atlas = await team.createFirstAtlas();
} else {
team.name = data.team.name;
team.slackData = data.team;
team = await team.save();
}
if (user) {
user.slackAccessToken = data.access_token;
user.slackData = data.user;
user = await user.save();
} else {
// Existing user
user = await User.create({
user = await team.createUser({
slackId: data.user.id,
username: authData.user,
name: data.user.name,
@ -56,30 +70,11 @@ router.post('auth.slack', async (ctx) => {
});
}
// Team
let team = await Team.findOne({ where: { slackId: data.team.id } });
if (!team) {
team = await Team.create({
name: data.team.name,
slackId: data.team.id,
slackData: data.team,
});
} else {
// Update data
team.name = data.team.name;
team.slackData = data.team;
team = await team.save();
}
// Add to correct team
user.setTeam(team);
ctx.body = { data: {
user: await presentUser(user),
team: await presentTeam(team),
accessToken: user.getJwtToken(),
}};
console.log("enf")
});
export default router;

View File

@ -23,8 +23,8 @@ router.post('documents.info', auth({ require: false }), async (ctx) => {
if (document.private) {
if (!ctx.state.user) throw httpErrors.NotFound();
const team = await ctx.state.user.getTeam();
if (document.teamId !== team.id) {
const user = await ctx.state.user;
if (document.teamId !== user.teamId) {
throw httpErrors.NotFound();
}
@ -52,11 +52,10 @@ router.post('documents.create', auth(), async (ctx) => {
ctx.assertPresent(text, 'text is required');
const user = ctx.state.user;
const team = await user.getTeam();
const ownerAtlas = await Atlas.findOne({
where: {
id: atlas,
teamId: team.id,
teamId: user.teamId,
},
});
@ -64,7 +63,7 @@ router.post('documents.create', auth(), async (ctx) => {
const document = await Document.create({
atlasId: ownerAtlas.id,
teamId: team.id,
teamId: user.teamId,
userId: user.id,
title: title,
text: text,
@ -86,11 +85,10 @@ router.post('documents.update', auth(), async (ctx) => {
ctx.assertPresent(text, 'text is required');
const user = ctx.state.user;
const team = await user.getTeam();
let document = await Document.findOne({
where: {
id: id,
teamId: team.id,
teamId: user.teamId,
},
});
@ -112,11 +110,10 @@ router.post('documents.delete', auth(), async (ctx) => {
ctx.assertPresent(id, 'id is required');
const user = ctx.state.user;
const team = await user.getTeam();
let document = await Document.findOne({
where: {
id: id,
teamId: team.id,
teamId: user.teamId,
},
});

View File

@ -0,0 +1,14 @@
{
"development": {
"use_env_variable": "DATABASE_URL",
"dialect": "postgres"
},
"test": {
"use_env_variable": "DATABASE_URL",
"dialect": "postgres"
},
"production": {
"use_env_variable": "DATABASE_URL",
"dialect": "postgres"
}
}

View File

@ -0,0 +1,258 @@
'use strict';
module.exports = {
up: function (queryInterface, Sequelize) {
queryInterface.createTable('atlases', {
id:
{ type: 'UUID',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: true },
name:
{ type: 'CHARACTER VARYING',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
description:
{ type: 'CHARACTER VARYING',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
type:
{ type: 'CHARACTER VARYING',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
atlasStructure:
{ type: 'JSONB',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
createdAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
updatedAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
teamId:
{ type: 'UUID',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false } }
);
// documents
queryInterface.createTable('documents', {
id:
{ type: 'UUID',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: true },
urlId:
{ type: 'CHARACTER VARYING',
allowNull: false,
unique: true,
defaultValue: null,
special: [],
primaryKey: false },
private:
{ type: 'BOOLEAN',
allowNull: false,
defaultValue: true,
special: [],
primaryKey: false },
title:
{ type: 'CHARACTER VARYING',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
text:
{ type: 'TEXT',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
html:
{ type: 'TEXT',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
preview:
{ type: 'TEXT',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
createdAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
updatedAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
userId:
{ type: 'UUID',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
atlasId:
{ type: 'UUID',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
rootDocumentForId:
{ type: 'UUID',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
teamId:
{ type: 'UUID',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false }
});
queryInterface.createTable('teams', {
id:
{ type: 'UUID',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: true },
name:
{ type: 'CHARACTER VARYING',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
slackId:
{ type: 'CHARACTER VARYING',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: true },
slackData:
{ type: 'JSONB',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
createdAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
updatedAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false } }
);
queryInterface.createTable('users', {
id:
{ type: 'UUID',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: true },
email:
{ type: 'CHARACTER VARYING',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
username:
{ type: 'CHARACTER VARYING',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
name:
{ type: 'CHARACTER VARYING',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
isAdmin:
{ type: 'BOOLEAN',
allowNull: true,
defaultValue: false,
special: [],
primaryKey: false },
slackAccessToken:
{ type: 'bytea',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
slackId:
{ type: 'CHARACTER VARYING',
allowNull: false,
defaultValue: null,
unique: true,
special: [],
primaryKey: false },
slackData:
{ type: 'JSONB',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
jwtSecret:
{ type: 'bytea',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false },
createdAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
updatedAt:
{ type: 'TIMESTAMP WITH TIME ZONE',
allowNull: false,
defaultValue: null,
special: [],
primaryKey: false },
teamId:
{ type: 'UUID',
allowNull: true,
defaultValue: null,
special: [],
primaryKey: false }
});
},
down: function (queryInterface, Sequelize) {
queryInterface.dropAllTables();
}
};

View File

@ -2,7 +2,7 @@ import {
DataTypes,
sequelize,
} from '../sequelize';
import Team from './Team';
import Document from './Document';
const allowedAtlasTypes = [['atlas', 'journal']];
@ -11,8 +11,33 @@ const Atlas = sequelize.define('atlas', {
name: DataTypes.STRING,
description: DataTypes.STRING,
type: { type: DataTypes.STRING, validate: { isIn: allowedAtlasTypes }},
/* type: atlas */
atlasStructure: DataTypes.JSONB,
}, {
tableName: 'atlases',
hooks: {
// beforeValidate: (doc) => {
// doc.urlId = randomstring.generate(15);
// },
// beforeCreate: (doc) => {
// doc.html = convertToMarkdown(doc.text);
// doc.preview = truncateMarkdown(doc.text, 160);
// },
// beforeUpdate: (doc) => {
// doc.html = convertToMarkdown(doc.text);
// doc.preview = truncateMarkdown(doc.text, 160);
// },
},
instanceMethods: {
// buildUrl() {
// const slugifiedTitle = slug(this.title);
// return `${slugifiedTitle}-${this.urlId}`;
// }
}
});
Atlas.belongsTo(Team);
Atlas.hasMany(Document, { as: 'documents', foreignKey: 'atlasId' });
Atlas.hasOne(Document, { as: 'rootDocument', foreignKey: 'rootDocumentForId', constraints: false });
export default Atlas;

View File

@ -10,8 +10,6 @@ import {
import {
truncateMarkdown,
} from '../utils/truncate';
import Atlas from './Atlas';
import Team from './Team';
import User from './User';
slug.defaults.mode ='rfc3986';
@ -51,8 +49,6 @@ const Document = sequelize.define('document', {
}
});
Document.belongsTo(Atlas, { as: 'atlas' });
Document.belongsTo(Team);
Document.belongsTo(User);
export default Document;

View File

@ -2,6 +2,9 @@ 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 },
@ -9,6 +12,17 @@ const Team = sequelize.define('team', {
slackId: { type: DataTypes.STRING, unique: true },
slackData: DataTypes.JSONB,
}, {
instanceMethods: {
async createFirstAtlas() {
const atlas = await Atlas.create({
name: this.name,
description: 'Your first Atlas',
type: 'journal',
teamId: this.id,
});
return atlas;
}
},
indexes: [
{
unique: true,
@ -17,4 +31,8 @@ const Team = sequelize.define('team', {
],
});
Team.hasMany(Atlas, { as: 'atlases' });
Team.hasMany(Document, { as: 'documents' });
Team.hasMany(User, { as: 'users' });
export default Team;

View File

@ -4,7 +4,6 @@ import {
sequelize,
encryptedFields
} from '../sequelize';
import Team from './Team';
import JWT from 'jsonwebtoken';
@ -39,8 +38,5 @@ const setRandomJwtSecret = (model) => {
};
User.beforeCreate(setRandomJwtSecret);
User.belongsTo(Team);
sequelize.sync();
export default User;

View File

@ -1,5 +1,5 @@
import _orderBy from 'lodash.orderby';
import Document from './models/Document';
import { Document, Atlas } from './models';
export function presentUser(user) {
return new Promise(async (resolve, reject) => {
@ -65,12 +65,14 @@ export async function presentDocument(document, includeAtlas=false) {
private: document.private,
createdAt: document.createdAt,
updatedAt: document.updatedAt,
atlas: document.atlaId,
atlas: document.atlasId,
team: document.teamId,
}
if (includeAtlas) {
const atlas = await document.getAtlas();
const atlas = await Atlas.findOne({ where: {
id: document.atlasId,
}});
data.atlas = await presentAtlas(atlas, false);
}