AuditLogs sayfasında güncellemeler
This commit is contained in:
parent
6723ce4e66
commit
d9712403f8
15 changed files with 463 additions and 228 deletions
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.AuditLogs;
|
||||||
|
|
||||||
|
public class AuditLogActionDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public virtual Guid AuditLogId { get; protected set; }
|
||||||
|
public virtual string ServiceName { get; protected set; }
|
||||||
|
public virtual string MethodName { get; protected set; }
|
||||||
|
public virtual string Parameters { get; protected set; }
|
||||||
|
public virtual DateTime ExecutionTime { get; protected set; }
|
||||||
|
public virtual int ExecutionDuration { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Volo.Abp;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
using Volo.Abp.Application.Services;
|
||||||
|
using Volo.Abp.AuditLogging;
|
||||||
|
using static Kurs.Platform.Data.Seeds.SeedConsts;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.AuditLogs;
|
||||||
|
|
||||||
|
public interface IAuditLogAppService
|
||||||
|
: ICrudAppService<AuditLogDto, Guid>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(AppCodes.AuditLogs)]
|
||||||
|
public class AuditLogAppService
|
||||||
|
: CrudAppService<AuditLog, AuditLogDto, Guid>
|
||||||
|
, IAuditLogAppService
|
||||||
|
{
|
||||||
|
public AuditLogAppService(IAuditLogRepository auditLogRepository) : base(auditLogRepository)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AuditLogDto> GetAsync(Guid id)
|
||||||
|
{
|
||||||
|
var entity = await Repository.GetAsync(id, includeDetails: true);
|
||||||
|
|
||||||
|
return await MapToGetOutputDtoAsync(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<PagedResultDto<AuditLogDto>> GetListAsync(PagedAndSortedResultRequestDto input)
|
||||||
|
{
|
||||||
|
var query = await CreateFilteredQueryAsync(input);
|
||||||
|
|
||||||
|
var totalCount = await AsyncExecuter.CountAsync(query);
|
||||||
|
|
||||||
|
query = ApplySorting(query, input);
|
||||||
|
query = ApplyPaging(query, input);
|
||||||
|
|
||||||
|
var auditLogList = await AsyncExecuter.ToListAsync(query);
|
||||||
|
|
||||||
|
var entityDtos = new List<AuditLogDto>();
|
||||||
|
foreach (var item in auditLogList)
|
||||||
|
{
|
||||||
|
var dto = await MapToGetListOutputDtoAsync(item);
|
||||||
|
dto.EntityChangeCount = item.EntityChanges?.Count ?? 0; // null kontrolü artık burada güvenli
|
||||||
|
entityDtos.Add(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PagedResultDto<AuditLogDto>(
|
||||||
|
totalCount,
|
||||||
|
entityDtos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audit Log kayitlarini gormek istiyoruz fakat degistirmek istemiyoruz
|
||||||
|
[RemoteService(IsEnabled = false)]
|
||||||
|
public override Task<AuditLogDto> CreateAsync(AuditLogDto input)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RemoteService(IsEnabled = false)]
|
||||||
|
public override Task<AuditLogDto> UpdateAsync(Guid id, AuditLogDto input)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RemoteService(IsEnabled = false)]
|
||||||
|
public override Task DeleteAsync(Guid id)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
api/src/Kurs.Platform.Application/AuditLogs/AuditLogDto.cs
Normal file
35
api/src/Kurs.Platform.Application/AuditLogs/AuditLogDto.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.AuditLogs;
|
||||||
|
|
||||||
|
public class AuditLogDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public AuditLogDto()
|
||||||
|
{
|
||||||
|
Actions = new List<AuditLogActionDto>();
|
||||||
|
EntityChanges = new List<EntityChangeDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ApplicationName { get; set; }
|
||||||
|
public Guid? UserId { get; protected set; }
|
||||||
|
public string UserName { get; protected set; }
|
||||||
|
public DateTime ExecutionTime { get; protected set; }
|
||||||
|
public int ExecutionDuration { get; protected set; }
|
||||||
|
public string ClientIpAddress { get; protected set; }
|
||||||
|
public string ClientName { get; protected set; }
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
public string CorrelationId { get; set; }
|
||||||
|
public string BrowserInfo { get; protected set; }
|
||||||
|
public string HttpMethod { get; protected set; }
|
||||||
|
public string Url { get; protected set; }
|
||||||
|
public string Exceptions { get; protected set; }
|
||||||
|
public string Comments { get; protected set; }
|
||||||
|
public int? HttpStatusCode { get; set; }
|
||||||
|
public ICollection<EntityChangeDto> EntityChanges { get; protected set; }
|
||||||
|
public ICollection<AuditLogActionDto> Actions { get; protected set; }
|
||||||
|
|
||||||
|
public int? EntityChangeCount { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
using Volo.Abp.Auditing;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.AuditLogs;
|
||||||
|
|
||||||
|
public class EntityChangeDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public EntityChangeDto()
|
||||||
|
{
|
||||||
|
PropertyChanges = new List<EntityPropertyChangeDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Guid AuditLogId { get; protected set; }
|
||||||
|
public virtual DateTime ChangeTime { get; protected set; }
|
||||||
|
public virtual EntityChangeType ChangeType { get; protected set; }
|
||||||
|
public virtual Guid? EntityTenantId { get; protected set; }
|
||||||
|
public virtual string EntityId { get; protected set; }
|
||||||
|
public virtual string EntityTypeFullName { get; protected set; }
|
||||||
|
public virtual ICollection<EntityPropertyChangeDto> PropertyChanges { get; protected set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using Volo.Abp.Application.Dtos;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.AuditLogs;
|
||||||
|
|
||||||
|
public class EntityPropertyChangeDto : EntityDto<Guid>
|
||||||
|
{
|
||||||
|
public virtual Guid EntityChangeId { get; protected set; }
|
||||||
|
public virtual string NewValue { get; protected set; }
|
||||||
|
public virtual string OriginalValue { get; protected set; }
|
||||||
|
public virtual string PropertyName { get; protected set; }
|
||||||
|
public virtual string PropertyTypeFullName { get; protected set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using AutoMapper;
|
||||||
|
using Volo.Abp.AuditLogging;
|
||||||
|
|
||||||
|
namespace Kurs.Platform.AuditLogs;
|
||||||
|
|
||||||
|
public class LogsAutoMapperProfile : Profile
|
||||||
|
{
|
||||||
|
public LogsAutoMapperProfile()
|
||||||
|
{
|
||||||
|
CreateMap<AuditLog, AuditLogDto>();
|
||||||
|
CreateMap<AuditLogAction, AuditLogActionDto>();
|
||||||
|
CreateMap<EntityChange, EntityChangeDto>();
|
||||||
|
CreateMap<EntityPropertyChange, EntityPropertyChangeDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7341,6 +7341,17 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
|
||||||
LoadPanelEnabled = "auto",
|
LoadPanelEnabled = "auto",
|
||||||
LoadPanelText = "Loading..."
|
LoadPanelText = "Loading..."
|
||||||
}),
|
}),
|
||||||
|
CommandColumnJson = JsonSerializer.Serialize(new CommandColumnDto[] {
|
||||||
|
new CommandColumnDto() {
|
||||||
|
Hint = "Details",
|
||||||
|
Text = "Details",
|
||||||
|
AuthName = AppCodes.AuditLogs,
|
||||||
|
DialogName = "AuditLogDetail",
|
||||||
|
DialogParameters = JsonSerializer.Serialize(new {
|
||||||
|
id = "@Id",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -7584,183 +7595,6 @@ public class ListFormsSeeder : IDataSeedContributor, ITransientDependency
|
||||||
#endregion
|
#endregion
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Entity Changes
|
|
||||||
var listFormEntityChange = await _listFormRepository.InsertAsync(
|
|
||||||
new ListForm()
|
|
||||||
{
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
ListFormCode = ListFormCodes.EntityChange,
|
|
||||||
Name = AppCodes.EntityChanges,
|
|
||||||
Title = AppCodes.EntityChanges,
|
|
||||||
DataSourceCode = SeedConsts.DataSources.DefaultCode,
|
|
||||||
IsTenant = true,
|
|
||||||
IsOrganizationUnit = false,
|
|
||||||
Description = AppCodes.EntityChanges,
|
|
||||||
SelectCommandType = SelectCommandTypeEnum.Table,
|
|
||||||
SelectCommand = "AbpEntityChanges",
|
|
||||||
KeyFieldName = "Id",
|
|
||||||
KeyFieldDbSourceType = DbType.Guid,
|
|
||||||
SortMode = GridOptions.SortModeSingle,
|
|
||||||
FilterRowJson = JsonSerializer.Serialize(new GridFilterRowDto
|
|
||||||
{
|
|
||||||
Visible = true
|
|
||||||
}),
|
|
||||||
HeaderFilterJson = JsonSerializer.Serialize(new
|
|
||||||
{
|
|
||||||
Visible = true
|
|
||||||
}),
|
|
||||||
SearchPanelJson = JsonSerializer.Serialize(new
|
|
||||||
{
|
|
||||||
Visible = true
|
|
||||||
}),
|
|
||||||
GroupPanelJson = JsonSerializer.Serialize(new
|
|
||||||
{
|
|
||||||
Visible = true
|
|
||||||
}),
|
|
||||||
SelectionJson = JsonSerializer.Serialize(new SelectionDto
|
|
||||||
{
|
|
||||||
Mode = GridOptions.SelectionModeSingle,
|
|
||||||
AllowSelectAll = false
|
|
||||||
}),
|
|
||||||
ColumnOptionJson = JsonSerializer.Serialize(new
|
|
||||||
{
|
|
||||||
ColumnFixingEnabled = true,
|
|
||||||
ColumnChooserEnabled = true
|
|
||||||
}),
|
|
||||||
PermissionJson = JsonSerializer.Serialize(new PermissionCrudDto
|
|
||||||
{
|
|
||||||
C = AppCodes.EntityChanges + ".Create",
|
|
||||||
R = AppCodes.EntityChanges,
|
|
||||||
U = AppCodes.EntityChanges + ".Update",
|
|
||||||
D = AppCodes.EntityChanges + ".Delete",
|
|
||||||
E = AppCodes.EntityChanges + ".Export",
|
|
||||||
}),
|
|
||||||
PagerOptionJson = JsonSerializer.Serialize(new GridPagerOptionDto
|
|
||||||
{
|
|
||||||
Visible = true,
|
|
||||||
AllowedPageSizes = "10,20,50,100",
|
|
||||||
ShowPageSizeSelector = true,
|
|
||||||
ShowNavigationButtons = true,
|
|
||||||
ShowInfo = false,
|
|
||||||
InfoText = "Page {0} of {1} ({2} items)",
|
|
||||||
DisplayMode = GridColumnOptions.PagerDisplayModeAdaptive,
|
|
||||||
ScrollingMode = GridColumnOptions.ScrollingModeStandard,
|
|
||||||
LoadPanelEnabled = "auto",
|
|
||||||
LoadPanelText = "Loading..."
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#region Entity Changes Fields
|
|
||||||
await _listFormFieldRepository.InsertManyAsync(
|
|
||||||
[
|
|
||||||
new() {
|
|
||||||
ListFormCode = listFormEntityChange.ListFormCode,
|
|
||||||
RoleId = null,
|
|
||||||
UserId = null,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.Guid,
|
|
||||||
FieldName = "Id",
|
|
||||||
Width = 100,
|
|
||||||
ListOrderNo = 1,
|
|
||||||
Visible = false,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
|
|
||||||
{
|
|
||||||
C = AppCodes.AuditLogs + ".Create",
|
|
||||||
R = AppCodes.AuditLogs,
|
|
||||||
U = AppCodes.AuditLogs + ".Update",
|
|
||||||
E = true,
|
|
||||||
Deny = false
|
|
||||||
}),
|
|
||||||
PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto
|
|
||||||
{
|
|
||||||
IsPivot = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
new() {
|
|
||||||
ListFormCode = listFormEntityChange.ListFormCode,
|
|
||||||
RoleId = null,
|
|
||||||
UserId = null,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.DateTime,
|
|
||||||
FieldName = "ChangeTime",
|
|
||||||
Width = 150,
|
|
||||||
ListOrderNo = 2,
|
|
||||||
Visible = true,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
AllowSearch = true,
|
|
||||||
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
|
|
||||||
{
|
|
||||||
C = AppCodes.AuditLogs + ".Create",
|
|
||||||
R = AppCodes.AuditLogs,
|
|
||||||
U = AppCodes.AuditLogs + ".Update",
|
|
||||||
E = true,
|
|
||||||
Deny = false
|
|
||||||
}),
|
|
||||||
PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto
|
|
||||||
{
|
|
||||||
IsPivot = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
new() {
|
|
||||||
ListFormCode = listFormEntityChange.ListFormCode,
|
|
||||||
RoleId = null,
|
|
||||||
UserId = null,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.String,
|
|
||||||
FieldName = "ChangeType",
|
|
||||||
Width = 125,
|
|
||||||
ListOrderNo = 3,
|
|
||||||
Visible = true,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
AllowSearch = true,
|
|
||||||
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
|
|
||||||
{
|
|
||||||
C = AppCodes.AuditLogs + ".Create",
|
|
||||||
R = AppCodes.AuditLogs,
|
|
||||||
U = AppCodes.AuditLogs + ".Update",
|
|
||||||
E = true,
|
|
||||||
Deny = false
|
|
||||||
}),
|
|
||||||
PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto
|
|
||||||
{
|
|
||||||
IsPivot = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
new() {
|
|
||||||
ListFormCode = listFormEntityChange.ListFormCode,
|
|
||||||
RoleId = null,
|
|
||||||
UserId = null,
|
|
||||||
CultureName = LanguageCodes.En,
|
|
||||||
SourceDbType = DbType.String,
|
|
||||||
FieldName = "EntityTypeFullName",
|
|
||||||
Width = 300,
|
|
||||||
ListOrderNo = 4,
|
|
||||||
Visible = true,
|
|
||||||
IsActive = true,
|
|
||||||
IsDeleted = false,
|
|
||||||
AllowSearch = true,
|
|
||||||
PermissionJson = JsonSerializer.Serialize(new ListFormFieldPermissionDto
|
|
||||||
{
|
|
||||||
C = AppCodes.AuditLogs + ".Create",
|
|
||||||
R = AppCodes.AuditLogs,
|
|
||||||
U = AppCodes.AuditLogs + ".Update",
|
|
||||||
E = true,
|
|
||||||
Deny = false
|
|
||||||
}),
|
|
||||||
PivotSettingsJson = JsonSerializer.Serialize(new ListFormFieldPivotSettingsDto
|
|
||||||
{
|
|
||||||
IsPivot = true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
#endregion
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Branches
|
#region Branches
|
||||||
var listFormBranches = await _listFormRepository.InsertAsync(
|
var listFormBranches = await _listFormRepository.InsertAsync(
|
||||||
new ListForm()
|
new ListForm()
|
||||||
|
|
|
||||||
|
|
@ -501,22 +501,6 @@ public class MenuSeeder : IDataSeedContributor, ITransientDependency
|
||||||
RequiredPermissionName = AppCodes.AuditLogs
|
RequiredPermissionName = AppCodes.AuditLogs
|
||||||
});
|
});
|
||||||
|
|
||||||
//Entity Changes
|
|
||||||
await _repository.InsertAsync(new Menu
|
|
||||||
{
|
|
||||||
Code = AppCodes.EntityChanges,
|
|
||||||
DisplayName = AppCodes.EntityChanges,
|
|
||||||
Order = 9,
|
|
||||||
IsDisabled = false,
|
|
||||||
ParentCode = menuAdministration.Code,
|
|
||||||
Icon = "FcSurvey",
|
|
||||||
Target = null,
|
|
||||||
ElementId = null,
|
|
||||||
CssClass = null,
|
|
||||||
Url = $"/list/{PlatformConsts.ListFormCodes.EntityChange}",
|
|
||||||
RequiredPermissionName = AppCodes.EntityChanges
|
|
||||||
});
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -837,39 +837,6 @@ public class PermissionSeeder : IDataSeedContributor, ITransientDependency
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region EntityChanges
|
|
||||||
await repositoryGroup.InsertAsync(new PermissionGroupDefinitionRecord
|
|
||||||
{
|
|
||||||
Name = AppCodes.EntityChanges,
|
|
||||||
DisplayName = AppCodes.EntityChanges
|
|
||||||
});
|
|
||||||
|
|
||||||
var permEntityChanges = await repository.InsertAsync(
|
|
||||||
GetNewPermission(
|
|
||||||
AppCodes.EntityChanges,
|
|
||||||
AppCodes.EntityChanges,
|
|
||||||
AppCodes.EntityChanges,
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
MultiTenancySides.Both
|
|
||||||
));
|
|
||||||
|
|
||||||
foreach (var item in CUDE)
|
|
||||||
{
|
|
||||||
permissionName = AppCodes.EntityChanges + "." + item;
|
|
||||||
|
|
||||||
await repository.InsertAsync(
|
|
||||||
GetNewPermission(
|
|
||||||
AppCodes.EntityChanges,
|
|
||||||
permissionName,
|
|
||||||
item,
|
|
||||||
permEntityChanges.Name,
|
|
||||||
true,
|
|
||||||
MultiTenancySides.Both
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Branches
|
#region Branches
|
||||||
await repositoryGroup.InsertAsync(new PermissionGroupDefinitionRecord
|
await repositoryGroup.InsertAsync(new PermissionGroupDefinitionRecord
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,6 @@ public static class PlatformConsts
|
||||||
public const string GlobalSearch = "List-0018";
|
public const string GlobalSearch = "List-0018";
|
||||||
public const string SecurityLog = "List-0019";
|
public const string SecurityLog = "List-0019";
|
||||||
public const string AuditLog = "List-0020";
|
public const string AuditLog = "List-0020";
|
||||||
public const string EntityChange = "List-0021";
|
|
||||||
public const string Branch = "List-0022";
|
public const string Branch = "List-0022";
|
||||||
public const string ListformField = "List-1000";
|
public const string ListformField = "List-1000";
|
||||||
public const string Order = "List-Order";
|
public const string Order = "List-Order";
|
||||||
|
|
|
||||||
|
|
@ -362,7 +362,6 @@ public static class SeedConsts
|
||||||
public const string IpRestrictions = Prefix.App + ".IpRestrictions";
|
public const string IpRestrictions = Prefix.App + ".IpRestrictions";
|
||||||
public const string PublicApis = Prefix.App + ".PublicApis";
|
public const string PublicApis = Prefix.App + ".PublicApis";
|
||||||
public const string AuditLogs = Prefix.App + ".AuditLogs";
|
public const string AuditLogs = Prefix.App + ".AuditLogs";
|
||||||
public const string EntityChanges = Prefix.App + ".EntityChanges";
|
|
||||||
public const string Branches = Prefix.App + ".Branches";
|
public const string Branches = Prefix.App + ".Branches";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
UserInfoViewModel,
|
UserInfoViewModel,
|
||||||
} from '@/proxy/admin'
|
} from '@/proxy/admin'
|
||||||
import apiService from '@/services/api.service'
|
import apiService from '@/services/api.service'
|
||||||
|
import { AuditLogDto } from '../auditLog/audit-log'
|
||||||
|
|
||||||
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
export const getRoles = (skipCount = 0, maxResultCount = 10) =>
|
||||||
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
apiService.fetchData<ListResultDto<IdentityRoleDto>>({
|
||||||
|
|
@ -65,3 +66,9 @@ export const updatePermissions = (
|
||||||
url: `/api/permission-management/permissions?providerName=${providerName}&providerKey=${providerKey}`,
|
url: `/api/permission-management/permissions?providerName=${providerName}&providerKey=${providerKey}`,
|
||||||
data: input,
|
data: input,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const getAuditLogs = (id: string) =>
|
||||||
|
apiService.fetchData<AuditLogDto>({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/api/app/audit-log/${id}`,
|
||||||
|
})
|
||||||
44
ui/src/proxy/auditLog/audit-log.ts
Normal file
44
ui/src/proxy/auditLog/audit-log.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
export interface AuditLogActionDto {
|
||||||
|
serviceName: string
|
||||||
|
methodName: string
|
||||||
|
executionTime: string // Date string
|
||||||
|
executionDuration: number
|
||||||
|
parameters: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EntityPropertyChangeDto {
|
||||||
|
propertyName: string
|
||||||
|
propertyTypeFullName: string
|
||||||
|
originalValue: string | null
|
||||||
|
newValue: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EntityChangeDto {
|
||||||
|
changeTime: string // Date string
|
||||||
|
changeType: number
|
||||||
|
entityId: string
|
||||||
|
entityTypeFullName: string
|
||||||
|
propertyChanges: EntityPropertyChangeDto[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditLogDto {
|
||||||
|
id: string
|
||||||
|
applicationName: string
|
||||||
|
userId?: string
|
||||||
|
userName?: string
|
||||||
|
executionTime: string
|
||||||
|
executionDuration: number
|
||||||
|
clientIpAddress?: string
|
||||||
|
clientName?: string
|
||||||
|
clientId?: string
|
||||||
|
correlationId?: string
|
||||||
|
browserInfo?: string
|
||||||
|
httpMethod?: string
|
||||||
|
url?: string
|
||||||
|
exceptions?: string
|
||||||
|
comments?: string
|
||||||
|
httpStatusCode?: number
|
||||||
|
entityChangeCount?: number
|
||||||
|
entityChanges: EntityChangeDto[]
|
||||||
|
actions: AuditLogActionDto[]
|
||||||
|
}
|
||||||
214
ui/src/views/admin/auditLog/AuditLogDetail.tsx
Normal file
214
ui/src/views/admin/auditLog/AuditLogDetail.tsx
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import Dialog from '@/components/ui/Dialog'
|
||||||
|
import TabList from '@/components/ui/Tabs/TabList'
|
||||||
|
import TabNav from '@/components/ui/Tabs/TabNav'
|
||||||
|
import TabContent from '@/components/ui/Tabs/TabContent'
|
||||||
|
import { Tabs } from '@/components/ui'
|
||||||
|
import { AdaptableCard } from '@/components/shared'
|
||||||
|
import { getAuditLogs } from '@/proxy/admin/identity.service'
|
||||||
|
import { AuditLogDto } from '@/proxy/auditLog/audit-log'
|
||||||
|
|
||||||
|
function AuditLogs({
|
||||||
|
open,
|
||||||
|
onDialogClose,
|
||||||
|
id,
|
||||||
|
}: {
|
||||||
|
open: boolean
|
||||||
|
onDialogClose: () => void
|
||||||
|
id: string
|
||||||
|
}) {
|
||||||
|
const [selectedLog, setSelectedLog] = useState<AuditLogDto>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && id) {
|
||||||
|
fetchAuditLog(id)
|
||||||
|
}
|
||||||
|
}, [open, id])
|
||||||
|
|
||||||
|
const fetchAuditLog = async (logId: string) => {
|
||||||
|
const response = await getAuditLogs(logId)
|
||||||
|
setSelectedLog(response.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog width="80%" isOpen={open} onClose={onDialogClose} onRequestClose={onDialogClose}>
|
||||||
|
<h5 className="text-lg font-semibold mb-4">
|
||||||
|
Audit Log Detail
|
||||||
|
{selectedLog?.id && (
|
||||||
|
<span className="text-xs font-normal text-slate-500 ml-2">({selectedLog.id})</span>
|
||||||
|
)}
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
{!selectedLog ? (
|
||||||
|
<div className="text-center py-6 text-gray-500">Loading...</div>
|
||||||
|
) : (
|
||||||
|
<Tabs defaultValue="log" variant="pill">
|
||||||
|
<TabList className="flex-wrap border-b mb-4 bg-slate-50 rounded-t">
|
||||||
|
<TabNav value="log">Log</TabNav>
|
||||||
|
<TabNav value="actions">
|
||||||
|
Actions {selectedLog.actions?.length > 0 && `(${selectedLog.actions.length})`}
|
||||||
|
</TabNav>
|
||||||
|
<TabNav value="changes">
|
||||||
|
Entity Changes{' '}
|
||||||
|
{selectedLog.entityChanges?.length > 0 && `(${selectedLog.entityChanges.length})`}
|
||||||
|
</TabNav>
|
||||||
|
</TabList>
|
||||||
|
|
||||||
|
{/* LOG DETAILS */}
|
||||||
|
<TabContent value="log">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 p-2">
|
||||||
|
<AdaptableCard>
|
||||||
|
<h6 className="font-medium mb-3 text-slate-600 text-sm">Request Info</h6>
|
||||||
|
<table className="w-full text-xs">
|
||||||
|
<tbody className="[&>tr>*]:py-0.5 [&>tr>td:first-child]:font-semibold [&>tr>td:first-child]:w-40 [&>tr>td:last-child]:break-all">
|
||||||
|
<tr>
|
||||||
|
<td>User</td>
|
||||||
|
<td>{selectedLog.userName}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Time</td>
|
||||||
|
<td>{new Date(selectedLog.executionTime).toLocaleString()}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Duration</td>
|
||||||
|
<td>{selectedLog.executionDuration} ms</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>IP</td>
|
||||||
|
<td>{selectedLog.clientIpAddress}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Method</td>
|
||||||
|
<td>{selectedLog.httpMethod}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Status Code</td>
|
||||||
|
<td>{selectedLog.httpStatusCode}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>URL</td>
|
||||||
|
<td>{selectedLog.url}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</AdaptableCard>
|
||||||
|
|
||||||
|
<AdaptableCard>
|
||||||
|
<h6 className="font-medium mb-3 text-slate-600 text-sm">Client Info</h6>
|
||||||
|
<table className="w-full text-xs">
|
||||||
|
<tbody className="[&>tr>*]:py-0.5 [&>tr>td:first-child]:font-semibold [&>tr>td:first-child]:w-40 [&>tr>td:last-child]:break-all">
|
||||||
|
<tr>
|
||||||
|
<td>Browser</td>
|
||||||
|
<td>{selectedLog.browserInfo}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Exceptions</td>
|
||||||
|
<td>
|
||||||
|
<pre className="bg-slate-100 text-red-500 p-2 rounded text-[11px] whitespace-pre-wrap leading-snug">
|
||||||
|
{selectedLog.exceptions || 'None'}
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</AdaptableCard>
|
||||||
|
</div>
|
||||||
|
</TabContent>
|
||||||
|
|
||||||
|
{/* ACTIONS */}
|
||||||
|
<TabContent value="actions">
|
||||||
|
{selectedLog.actions?.map((action, index) => (
|
||||||
|
<AdaptableCard key={index} className="mb-4">
|
||||||
|
<h6 className="font-medium text-slate-600 mb-3 text-sm">Action #{index + 1}</h6>
|
||||||
|
<table className="w-full text-xs">
|
||||||
|
<tbody className="[&>tr>*]:py-0.5 [&>tr>td:first-child]:font-semibold [&>tr>td:first-child]:w-40 [&>tr>td:last-child]:break-all">
|
||||||
|
<tr>
|
||||||
|
<td>Service</td>
|
||||||
|
<td>{action.serviceName}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Method</td>
|
||||||
|
<td>{action.methodName}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Time</td>
|
||||||
|
<td>{action.executionTime}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Duration</td>
|
||||||
|
<td>{action.executionDuration} ms</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Parameters</td>
|
||||||
|
<td>
|
||||||
|
<pre className="bg-slate-100 p-2 rounded text-[11px] whitespace-pre-wrap leading-snug">
|
||||||
|
{action.parameters}
|
||||||
|
</pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</AdaptableCard>
|
||||||
|
))}
|
||||||
|
</TabContent>
|
||||||
|
|
||||||
|
{/* ENTITY CHANGES */}
|
||||||
|
<TabContent value="changes">
|
||||||
|
{selectedLog.entityChanges?.map((change, index) => (
|
||||||
|
<AdaptableCard key={index} className="mb-4">
|
||||||
|
<h6 className="font-medium text-slate-600 mb-3 text-sm">Change #{index + 1}</h6>
|
||||||
|
<table className="w-full text-xs">
|
||||||
|
<tbody className="[&>tr>*]:py-0.5 [&>tr>td:first-child]:font-semibold [&>tr>td:first-child]:w-40 [&>tr>td:last-child]:break-all">
|
||||||
|
<tr>
|
||||||
|
<td>Time</td>
|
||||||
|
<td>{new Date(change.changeTime).toLocaleString()}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Type</td>
|
||||||
|
<td>{change.changeType}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Entity ID</td>
|
||||||
|
<td>{change.entityId}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Entity Type</td>
|
||||||
|
<td>{change.entityTypeFullName}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Property Changes</td>
|
||||||
|
<td>
|
||||||
|
<ul className="list-disc pl-4 space-y-1">
|
||||||
|
{change.propertyChanges.map((p, i) => (
|
||||||
|
<li key={i}>
|
||||||
|
<code>{p.propertyName}</code> ({p.propertyTypeFullName}):{' '}
|
||||||
|
{!p.originalValue || p.originalValue === 'null' ? (
|
||||||
|
<span className="text-green-600 font-semibold ml-1">
|
||||||
|
{p.newValue}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span className="text-gray-500">{p.originalValue}</span>{' '}
|
||||||
|
<span className="mx-1">→</span>
|
||||||
|
<span className="text-yellow-600 font-semibold">
|
||||||
|
{p.newValue}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</AdaptableCard>
|
||||||
|
))}
|
||||||
|
</TabContent>
|
||||||
|
</Tabs>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuditLogs
|
||||||
|
|
@ -3,6 +3,7 @@ import UsersPermission from '@/views/admin/identity/Users/UsersPermission'
|
||||||
import TenantsConnectionString from '@/views/admin/tenant-management/TenantsConnectionString'
|
import TenantsConnectionString from '@/views/admin/tenant-management/TenantsConnectionString'
|
||||||
import { useDialogContext } from './DialogProvider'
|
import { useDialogContext } from './DialogProvider'
|
||||||
import CreateNotification from '@/views/admin/notification/CreateNotification'
|
import CreateNotification from '@/views/admin/notification/CreateNotification'
|
||||||
|
import AuditLogDetail from '@/views/admin/auditLog/AuditLogDetail'
|
||||||
|
|
||||||
const DialogShowComponent = (): JSX.Element => {
|
const DialogShowComponent = (): JSX.Element => {
|
||||||
const dialogContext: any = useDialogContext()
|
const dialogContext: any = useDialogContext()
|
||||||
|
|
@ -41,6 +42,14 @@ const DialogShowComponent = (): JSX.Element => {
|
||||||
{...dialogContext.config?.props}
|
{...dialogContext.config?.props}
|
||||||
></CreateNotification>
|
></CreateNotification>
|
||||||
)
|
)
|
||||||
|
case 'AuditLogDetail':
|
||||||
|
return (
|
||||||
|
<AuditLogDetail
|
||||||
|
open={true}
|
||||||
|
onDialogClose={() => dialogContext.setConfig({})}
|
||||||
|
{...dialogContext.config?.props}
|
||||||
|
></AuditLogDetail>
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue