feat: Events / audit log (#1008)

* feat: Record events in DB

* feat: events API

* First pass, hacky activity feed

* WIP

* Reset dashboard

* feat: audit log UI
feat: store ip address

* chore: Document events.list api

* fix: command specs

* await event create

* fix: backlinks service

* tidy

* fix: Hide audit log menu item if not admin
This commit is contained in:
Tom Moor
2019-08-05 20:38:31 -07:00
committed by GitHub
parent 75b03fdba2
commit fb4f6822a4
37 changed files with 911 additions and 148 deletions

View File

@ -4,12 +4,11 @@ import Router from 'koa-router';
import auth from '../middlewares/authentication';
import pagination from './middlewares/pagination';
import { presentCollection, presentUser } from '../presenters';
import { Collection, CollectionUser, Team, User } from '../models';
import { Collection, CollectionUser, Team, Event, User } from '../models';
import { ValidationError, InvalidRequestError } from '../errors';
import { exportCollections } from '../logistics';
import { archiveCollection } from '../utils/zip';
import policy from '../policies';
import events from '../events';
const { authorize } = policy;
const router = new Router();
@ -35,11 +34,13 @@ router.post('collections.create', auth(), async ctx => {
private: isPrivate,
});
events.add({
await Event.create({
name: 'collections.create',
modelId: collection.id,
collectionId: collection.id,
teamId: collection.teamId,
actorId: user.id,
data: { name },
ip: ctx.request.ip,
});
ctx.body = {
@ -81,12 +82,14 @@ router.post('collections.add_user', auth(), async ctx => {
createdById: ctx.state.user.id,
});
events.add({
await Event.create({
name: 'collections.add_user',
modelId: userId,
userId,
collectionId: collection.id,
teamId: collection.teamId,
actorId: ctx.state.user.id,
data: { name: user.name },
ip: ctx.request.ip,
});
ctx.body = {
@ -111,12 +114,14 @@ router.post('collections.remove_user', auth(), async ctx => {
await collection.removeUser(user);
events.add({
await Event.create({
name: 'collections.remove_user',
modelId: userId,
userId,
collectionId: collection.id,
teamId: collection.teamId,
actorId: ctx.state.user.id,
data: { name: user.name },
ip: ctx.request.ip,
});
ctx.body = {
@ -148,6 +153,15 @@ router.post('collections.export', auth(), async ctx => {
const filePath = await archiveCollection(collection);
await Event.create({
name: 'collections.export',
collectionId: collection.id,
teamId: user.teamId,
actorId: user.id,
data: { title: collection.title },
ip: ctx.request.ip,
});
ctx.attachment(`${collection.name}.zip`);
ctx.set('Content-Type', 'application/force-download');
ctx.body = fs.createReadStream(filePath);
@ -161,6 +175,13 @@ router.post('collections.exportAll', auth(), async ctx => {
// async operation to create zip archive and email user
exportCollections(user.teamId, user.email);
await Event.create({
name: 'collections.export',
teamId: user.teamId,
actorId: user.id,
ip: ctx.request.ip,
});
ctx.body = {
success: true,
};
@ -197,11 +218,13 @@ router.post('collections.update', auth(), async ctx => {
collection.private = isPrivate;
await collection.save();
events.add({
await Event.create({
name: 'collections.update',
modelId: collection.id,
collectionId: collection.id,
teamId: collection.teamId,
actorId: user.id,
data: { name },
ip: ctx.request.ip,
});
ctx.body = {
@ -246,11 +269,13 @@ router.post('collections.delete', auth(), async ctx => {
await collection.destroy();
events.add({
await Event.create({
name: 'collections.delete',
modelId: collection.id,
collectionId: collection.id,
teamId: collection.teamId,
actorId: user.id,
data: { name: collection.name },
ip: ctx.request.ip,
});
ctx.body = {