diff --git a/frontend/components/DocumentViews/DocumentViewersStore.js b/frontend/components/DocumentViews/DocumentViewersStore.js index 8fda9223..0f620ffb 100644 --- a/frontend/components/DocumentViews/DocumentViewersStore.js +++ b/frontend/components/DocumentViews/DocumentViewersStore.js @@ -18,7 +18,7 @@ class DocumentViewersStore { this.isFetching = true; try { - const res = await client.get( + const res = await client.post( '/views.list', { id: this.documentId, diff --git a/index.js b/index.js index 0ac7c2d0..9f822e6c 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,13 @@ require('./init'); -var app = require('./server').default; -var http = require('http'); +const app = require('./server').default; +const http = require('http'); -var server = http.createServer(app.callback()); +const server = http.createServer(app.callback()); server.listen(process.env.PORT || '3000'); -server.on('error', (err) => { +server.on('error', err => { throw err; }); server.on('listening', () => { - var address = server.address(); - console.log('Listening on %s%s', address.address, address.port); -}); \ No newline at end of file + const address = server.address(); + console.log(`Listening on http://localhost:${address.port}`); +}); diff --git a/server/api/collections.js b/server/api/collections.js index b129c085..2b48f40f 100644 --- a/server/api/collections.js +++ b/server/api/collections.js @@ -24,7 +24,7 @@ router.post('collections.create', auth(), async ctx => { }); ctx.body = { - data: await presentCollection(ctx, atlas, true), + data: await presentCollection(ctx, atlas), }; }); @@ -33,7 +33,7 @@ router.post('collections.info', auth(), async ctx => { ctx.assertPresent(id, 'id is required'); const user = ctx.state.user; - const atlas = await Collection.findOne({ + const atlas = await Collection.scope('withRecentDocuments').findOne({ where: { id, teamId: user.teamId, @@ -43,7 +43,7 @@ router.post('collections.info', auth(), async ctx => { if (!atlas) throw httpErrors.NotFound(); ctx.body = { - data: await presentCollection(ctx, atlas, true), + data: await presentCollection(ctx, atlas), }; }); @@ -58,16 +58,10 @@ router.post('collections.list', auth(), pagination(), async ctx => { limit: ctx.state.pagination.limit, }); - // Collectiones - let data = []; - await Promise.all( - collections.map(async atlas => { - return data.push(await presentCollection(ctx, atlas, true)); - }) + const data = await Promise.all( + collections.map(async atlas => await presentCollection(ctx, atlas)) ); - data = _.orderBy(data, ['updatedAt'], ['desc']); - ctx.body = { pagination: ctx.state.pagination, data, diff --git a/server/api/documents.js b/server/api/documents.js index 5c8e3d7b..cf525861 100644 --- a/server/api/documents.js +++ b/server/api/documents.js @@ -8,7 +8,6 @@ import { presentDocument } from '../presenters'; import { Document, Collection, Star, View } from '../models'; const router = new Router(); - router.post('documents.list', auth(), pagination(), async ctx => { let { sort = 'updatedAt', direction } = ctx.body; if (direction !== 'ASC') direction = 'DESC'; @@ -19,9 +18,12 @@ router.post('documents.list', auth(), pagination(), async ctx => { order: [[sort, direction]], offset: ctx.state.pagination.offset, limit: ctx.state.pagination.limit, + include: [{ model: Star, as: 'starred', where: { userId: user.id } }], }); - let data = await Promise.all(documents.map(doc => presentDocument(ctx, doc))); + const data = await Promise.all( + documents.map(document => presentDocument(ctx, document)) + ); ctx.body = { pagination: ctx.state.pagination, @@ -42,7 +44,7 @@ router.post('documents.viewed', auth(), pagination(), async ctx => { limit: ctx.state.pagination.limit, }); - let data = await Promise.all( + const data = await Promise.all( views.map(view => presentDocument(ctx, view.document)) ); @@ -60,12 +62,17 @@ router.post('documents.starred', auth(), pagination(), async ctx => { const views = await Star.findAll({ where: { userId: user.id }, order: [[sort, direction]], - include: [{ model: Document }], + include: [ + { + model: Document, + include: [{ model: Star, as: 'starred', where: { userId: user.id } }], + }, + ], offset: ctx.state.pagination.offset, limit: ctx.state.pagination.limit, }); - let data = await Promise.all( + const data = await Promise.all( views.map(view => presentDocument(ctx, view.document)) ); @@ -94,8 +101,7 @@ router.post('documents.info', auth(), async ctx => { ctx.body = { data: await presentDocument(ctx, document, { - includeCollection: document.private, - includeCollaborators: true, + includeViews: true, }), }; }); @@ -108,16 +114,8 @@ 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, - }) - ); - }) + const data = await Promise.all( + documents.map(async document => await presentDocument(ctx, document)) ); ctx.body = { @@ -200,11 +198,7 @@ router.post('documents.create', auth(), async ctx => { } ctx.body = { - data: await presentDocument(ctx, newDocument, { - includeCollection: true, - includeCollaborators: true, - collection: ownerCollection, - }), + data: await presentDocument(ctx, newDocument), }; }); @@ -230,11 +224,7 @@ router.post('documents.update', auth(), async ctx => { } ctx.body = { - data: await presentDocument(ctx, document, { - includeCollection: true, - includeCollaborators: true, - collection: collection, - }), + data: await presentDocument(ctx, document), }; }); @@ -273,11 +263,7 @@ router.post('documents.move', auth(), async ctx => { } ctx.body = { - data: await presentDocument(ctx, document, { - includeCollection: true, - includeCollaborators: true, - collection: collection, - }), + data: await presentDocument(ctx, document), }; }); diff --git a/server/middlewares/cache.js b/server/middlewares/cache.js index 7a10ad64..22068f62 100644 --- a/server/middlewares/cache.js +++ b/server/middlewares/cache.js @@ -1,9 +1,10 @@ +// @flow import debug from 'debug'; const debugCache = debug('cache'); export default function cache() { - return async function cacheMiddleware(ctx, next) { + return async function cacheMiddleware(ctx: Object, next: Function) { ctx.cache = {}; ctx.cache.set = async (id, value) => { diff --git a/server/models/Collection.js b/server/models/Collection.js index 953a57ae..382b5476 100644 --- a/server/models/Collection.js +++ b/server/models/Collection.js @@ -60,6 +60,16 @@ const Collection = sequelize.define( as: 'documents', foreignKey: 'atlasId', }); + Collection.addScope('withRecentDocuments', { + include: [ + { + as: 'documents', + limit: 10, + model: models.Document, + order: [['updatedAt', 'DESC']], + }, + ], + }); }, }, instanceMethods: { diff --git a/server/models/Document.js b/server/models/Document.js index d94fe7f5..9e7c44a3 100644 --- a/server/models/Document.js +++ b/server/models/Document.js @@ -115,7 +115,32 @@ const Document = sequelize.define( }, classMethods: { associate: models => { - Document.belongsTo(models.User); + Document.belongsTo(models.Collection, { + as: 'collection', + foreignKey: 'atlasId', + }); + Document.belongsTo(models.User, { + as: 'createdBy', + foreignKey: 'createdById', + }); + Document.belongsTo(models.User, { + as: 'updatedBy', + foreignKey: 'lastModifiedById', + }); + Document.hasMany(models.Star, { + as: 'starred', + }); + Document.addScope( + 'defaultScope', + { + include: [ + { model: models.Collection, as: 'collection' }, + { model: models.User, as: 'createdBy' }, + { model: models.User, as: 'updatedBy' }, + ], + }, + { override: true } + ); }, findById: async id => { if (isUUID(id)) { diff --git a/server/presenters/collection.js b/server/presenters/collection.js index 8dc1777d..a6fdbc8e 100644 --- a/server/presenters/collection.js +++ b/server/presenters/collection.js @@ -1,8 +1,9 @@ +// @flow import _ from 'lodash'; -import { Document } from '../models'; +import { Collection } from '../models'; import presentDocument from './document'; -async function present(ctx, collection, includeRecentDocuments = false) { +async function present(ctx: Object, collection: Collection) { ctx.cache.set(collection.id, collection); const data = { @@ -13,31 +14,21 @@ async function present(ctx, collection, includeRecentDocuments = false) { type: collection.type, createdAt: collection.createdAt, updatedAt: collection.updatedAt, + recentDocuments: undefined, + documents: undefined, }; - if (collection.type === 'atlas') + if (collection.type === 'atlas') { data.documents = await collection.getDocumentsStructure(); + } - if (includeRecentDocuments) { - const documents = await Document.findAll({ - where: { - atlasId: collection.id, - }, - limit: 10, - order: [['updatedAt', 'DESC']], - }); - - const recentDocuments = []; - await Promise.all( - documents.map(async document => { - recentDocuments.push( - await presentDocument(ctx, document, { - includeCollaborators: true, - }) - ); - }) + if (collection.documents) { + data.recentDocuments = await Promise.all( + collection.documents.map( + async document => + await presentDocument(ctx, document, { includeCollaborators: true }) + ) ); - data.recentDocuments = _.orderBy(recentDocuments, ['updatedAt'], ['desc']); } return data; diff --git a/server/presenters/document.js b/server/presenters/document.js index 8757ed7e..e89658a5 100644 --- a/server/presenters/document.js +++ b/server/presenters/document.js @@ -1,17 +1,16 @@ -import { Collection, Star, User, View } from '../models'; +// @flow +import _ from 'lodash'; +import { User, Document, View } from '../models'; import presentUser from './user'; import presentCollection from './collection'; -async function present(ctx, document, options) { +async function present(ctx: Object, document: Document, options: Object = {}) { options = { - includeCollection: true, includeCollaborators: true, - includeViews: true, + includeViews: false, ...options, }; ctx.cache.set(document.id, document); - - const userId = ctx.state.user.id; const data = { id: document.id, url: document.getUrl(), @@ -21,16 +20,19 @@ async function present(ctx, document, options) { html: document.html, preview: document.preview, createdAt: document.createdAt, - createdBy: undefined, + createdBy: presentUser(ctx, document.createdBy), updatedAt: document.updatedAt, - updatedBy: undefined, + updatedBy: presentUser(ctx, document.updatedBy), team: document.teamId, collaborators: [], + starred: !!document.starred, + collection: undefined, + views: undefined, }; - data.starred = !!await Star.findOne({ - where: { documentId: document.id, userId }, - }); + if (document.private) { + data.collection = await presentCollection(ctx, document.collection); + } if (options.includeViews) { data.views = await View.sum('count', { @@ -38,42 +40,15 @@ async function present(ctx, document, options) { }); } - if (options.includeCollection) { - data.collection = await ctx.cache.get(document.atlasId, async () => { - const collection = - options.collection || - (await Collection.findOne({ - where: { - id: document.atlasId, - }, - })); - return presentCollection(ctx, collection); - }); - } - if (options.includeCollaborators) { // This could be further optimized by using ctx.cache data.collaborators = await User.findAll({ where: { - id: { - $in: document.collaboratorIds || [], - }, + id: { $in: _.takeRight(document.collaboratorIds, 10) || [] }, }, }).map(user => presentUser(ctx, user)); } - const createdBy = await ctx.cache.get( - document.createdById, - async () => await User.findById(document.createdById) - ); - data.createdBy = await presentUser(ctx, createdBy); - - const updatedBy = await ctx.cache.get( - document.lastModifiedById, - async () => await User.findById(document.lastModifiedById) - ); - data.updatedBy = await presentUser(ctx, updatedBy); - return data; } diff --git a/server/presenters/team.js b/server/presenters/team.js index c418192b..6587798a 100644 --- a/server/presenters/team.js +++ b/server/presenters/team.js @@ -1,4 +1,7 @@ -function present(ctx, team) { +// @flow +import { Team } from '../models'; + +function present(ctx: Object, team: Team) { ctx.cache.set(team.id, team); return {