Skip to content

Commit

Permalink
Merge pull request #444 from internxt/feat/access-logs
Browse files Browse the repository at this point in the history
[PB-3334] Feat/access logs
  • Loading branch information
sg-gs authored Dec 20, 2024
2 parents 349ce80 + c3c4fec commit 6bc73d9
Show file tree
Hide file tree
Showing 19 changed files with 2,362 additions and 8 deletions.
96 changes: 96 additions & 0 deletions migrations/20241113173746-create-workspace-logs-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict';

const tableName = 'workspace_logs';
const indexName = 'workspace_id_workspace_logs_index';
const indexName2 = 'created_at_workspace_logs_index';
const indexName3 = 'platform_workspace_logs_index';
const indexName4 = 'creator_workspace_logs_index';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(tableName, {
id: {
type: Sequelize.DataTypes.UUID,
primaryKey: true,
defaultValue: Sequelize.DataTypes.UUIDV4,
},
workspace_id: {
type: Sequelize.DataTypes.UUID,
allowNull: false,
references: {
model: 'workspaces',
key: 'id',
},
onDelete: 'CASCADE',
},
creator: {
type: Sequelize.STRING(36),
allowNull: false,
references: {
model: 'users',
key: 'uuid',
},
onDelete: 'CASCADE',
},
type: {
type: Sequelize.ENUM(
'login',
'changed-password',
'logout',
'share-file',
'share-folder',
'delete-file',
'delete-folder',
),
allowNull: false,
},
platform: {
type: Sequelize.ENUM(
'web',
'mobile',
'desktop',
),
allowNull: false,
},
entity_id: {
type: Sequelize.DataTypes.UUID,
allowNull: true,
},
created_at: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
updated_at: {
type: Sequelize.DataTypes.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
});
await queryInterface.addIndex(tableName, {
fields: ['workspace_id'],
name: indexName,
});
await queryInterface.addIndex(tableName, {
fields: ['created_at'],
name: indexName2,
});
await queryInterface.addIndex(tableName, {
fields: ['platform'],
name: indexName3,
});
await queryInterface.addIndex(tableName, {
fields: ['creator'],
name: indexName4,
});
},

async down(queryInterface) {
await queryInterface.removeIndex(tableName, indexName);
await queryInterface.removeIndex(tableName, indexName2);
await queryInterface.removeIndex(tableName, indexName3);
await queryInterface.removeIndex(tableName, indexName4);
await queryInterface.dropTable(tableName);
},
};
4 changes: 4 additions & 0 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { Client } from './decorators/client.decorator';
import { TwoFactorAuthService } from './two-factor-auth.service';
import { DeleteTfaDto } from './dto/delete-tfa.dto';
import { UpdateTfaDto } from './dto/update-tfa.dto';
import { WorkspaceLogAction } from '../workspaces/decorators/workspace-log-action.decorator';
import { WorkspaceLogType } from '../workspaces/attributes/workspace-logs.attributes';

@ApiTags('Auth')
@Controller('auth')
Expand Down Expand Up @@ -89,6 +91,7 @@ export class AuthController {
description: 'User successfully accessed their account',
})
@Public()
@WorkspaceLogAction(WorkspaceLogType.Login)
async loginAccess(@Body() body: LoginAccessDto) {
return this.userUseCases.loginAccess(body);
}
Expand All @@ -100,6 +103,7 @@ export class AuthController {
summary: 'Log out of the account',
})
@ApiOkResponse({ description: 'Successfully logged out' })
@WorkspaceLogAction(WorkspaceLogType.Logout)
async logout(@UserDecorator() user: User) {
return { logout: true };
}
Expand Down
4 changes: 3 additions & 1 deletion src/modules/sharing/sharing.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
Headers,
Patch,
UseFilters,
InternalServerErrorException,
} from '@nestjs/common';
import { Response } from 'express';
import {
Expand Down Expand Up @@ -64,6 +63,8 @@ import {
WorkspacesInBehalfGuard,
} from '../workspaces/guards/workspaces-resources-in-behalf.decorator';
import { GetDataFromRequest } from '../../common/extract-data-from-request';
import { WorkspaceLogAction } from '../workspaces/decorators/workspace-log-action.decorator';
import { WorkspaceLogGlobalActionType } from '../workspaces/attributes/workspace-logs.attributes';

@ApiTags('Sharing')
@Controller('sharings')
Expand Down Expand Up @@ -619,6 +620,7 @@ export class SharingController {
{ sourceKey: 'body', fieldName: 'itemType' },
])
@WorkspacesInBehalfGuard()
@WorkspaceLogAction(WorkspaceLogGlobalActionType.Share)
createSharing(
@UserDecorator() user,
@Body() acceptInviteDto: CreateSharingDto,
Expand Down
3 changes: 3 additions & 0 deletions src/modules/trash/trash.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import { GetDataFromRequest } from '../../common/extract-data-from-request';
import { StorageNotificationService } from '../../externals/notifications/storage.notifications.service';
import { BasicPaginationDto } from '../../common/dto/basic-pagination.dto';
import { Requester } from '../auth/decorators/requester.decorator';
import { WorkspaceLogAction } from '../workspaces/decorators/workspace-log-action.decorator';
import { WorkspaceLogGlobalActionType } from '../workspaces/attributes/workspace-logs.attributes';

@ApiTags('Trash')
@Controller('storage/trash')
Expand Down Expand Up @@ -266,6 +268,7 @@ export class TrashController {
})
@GetDataFromRequest([{ sourceKey: 'body', fieldName: 'items' }])
@WorkspacesInBehalfGuard(WorkspaceResourcesAction.DeleteItemsFromTrash)
@WorkspaceLogAction(WorkspaceLogGlobalActionType.Delete)
async deleteItems(
@Body() deleteItemsDto: DeleteItemsDto,
@UserDecorator() user: User,
Expand Down
3 changes: 3 additions & 0 deletions src/modules/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ import { HttpExceptionFilter } from '../../lib/http/http-exception.filter';
import { RequestAccountUnblock } from './dto/account-unblock.dto';
import { RegisterNotificationTokenDto } from './dto/register-notification-token.dto';
import { getFutureIAT } from '../../middlewares/passport';
import { WorkspaceLogAction } from '../workspaces/decorators/workspace-log-action.decorator';
import { WorkspaceLogType } from '../workspaces/attributes/workspace-logs.attributes';

@ApiTags('User')
@Controller('users')
Expand Down Expand Up @@ -422,6 +424,7 @@ export class UserController {

@Patch('password')
@ApiBearerAuth()
@WorkspaceLogAction(WorkspaceLogType.ChangedPassword)
async updatePassword(
@RequestDecorator() req,
@Body() updatePasswordDto: UpdatePasswordDto,
Expand Down
36 changes: 36 additions & 0 deletions src/modules/workspaces/attributes/workspace-logs.attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export enum WorkspaceLogType {
Login = 'login',
ChangedPassword = 'changed-password',
Logout = 'logout',
ShareFile = 'share-file',
ShareFolder = 'share-folder',
DeleteFile = 'delete-file',
DeleteFolder = 'delete-folder',
}

export enum WorkspaceLogGlobalActionType {
Share = 'share',
Delete = 'delete',
DeleteAll = 'delete-all',
}

export enum WorkspaceLogPlatform {
Web = 'web',
Mobile = 'mobile',
Desktop = 'desktop',
}

export interface WorkspaceLogAttributes {
id: string;
workspaceId: string;
creator: string;
type: WorkspaceLogType;
platform: WorkspaceLogPlatform;
entityId?: string;
user?: any;
workspace?: any;
file?: any;
folder?: any;
createdAt: Date;
updatedAt: Date;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { applyDecorators, SetMetadata, UseInterceptors } from '@nestjs/common';
import {
WorkspaceLogGlobalActionType,
WorkspaceLogType,
} from '../attributes/workspace-logs.attributes';
import { WorkspacesLogsInterceptor } from './../interceptors/workspaces-logs.interceptor';

export const WorkspaceLogAction = (
action: WorkspaceLogType | WorkspaceLogGlobalActionType,
) =>
applyDecorators(
SetMetadata('workspaceLogAction', action),
UseInterceptors(WorkspacesLogsInterceptor),
);
65 changes: 65 additions & 0 deletions src/modules/workspaces/domains/workspace-log.domain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
WorkspaceLogAttributes,
WorkspaceLogPlatform,
WorkspaceLogType,
} from '../attributes/workspace-logs.attributes';

export class WorkspaceLog implements WorkspaceLogAttributes {
id: string;
workspaceId: string;
creator: string;
type: WorkspaceLogType;
platform: WorkspaceLogPlatform;
entityId?: string;
user?: any;
workspace?: any;
file?: any;
folder?: any;
createdAt: Date;
updatedAt: Date;

constructor({
id,
workspaceId,
creator,
type,
platform,
entityId,
user,
workspace,
file,
folder,
createdAt,
updatedAt,
}: WorkspaceLogAttributes) {
this.id = id;
this.workspaceId = workspaceId;
this.creator = creator;
this.type = type;
this.platform = platform;
this.entityId = entityId;
this.user = user;
this.workspace = workspace;
this.file = file;
this.folder = folder;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}

static build(user: WorkspaceLogAttributes): WorkspaceLog {
return new WorkspaceLog(user);
}

toJSON() {
return {
id: this.id,
workspaceId: this.workspaceId,
creator: this.creator,
type: this.type,
platform: this.platform,
entityId: this.entityId,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
};
}
}
60 changes: 60 additions & 0 deletions src/modules/workspaces/dto/get-workspace-logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ApiPropertyOptional } from '@nestjs/swagger';
import {
ArrayNotEmpty,
IsArray,
IsBoolean,
IsEnum,
IsInt,
IsOptional,
IsString,
Min,
Max,
} from 'class-validator';
import { OrderBy } from './../../../common/order.type';
import { WorkspaceLogType } from '../attributes/workspace-logs.attributes';
import { Transform, Type } from 'class-transformer';

export class GetWorkspaceLogsDto {
@IsOptional()
@IsArray()
@ArrayNotEmpty()
@IsEnum(WorkspaceLogType, { each: true })
activity?: WorkspaceLogType[];

@ApiPropertyOptional({
description: 'Order by',
example: 'name:asc',
})
@IsOptional()
@IsString()
orderBy?: OrderBy;

@IsOptional()
@IsInt()
@Min(1)
@Max(25)
@Type(() => Number)
limit?: number;

@IsOptional()
@IsInt()
@Min(0)
@Type(() => Number)
offset?: number;

@IsOptional()
@IsString()
member?: string;

@IsOptional()
@IsInt()
@Min(0)
@Max(90)
@Type(() => Number)
lastDays?: number;

@IsOptional()
@Transform(({ value }) => value === 'true')
@IsBoolean()
summary?: boolean = true;
}
Loading

0 comments on commit 6bc73d9

Please sign in to comment.