mirror of
https://github.com/haiwen/seahub.git
synced 2025-05-13 10:25:46 +00:00
add filter group audit
This commit is contained in:
parent
8d44d72d7d
commit
0fd229bfbe
frontend/src
pages
utils
seahub
@ -13,7 +13,7 @@ const propTypes = {
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
searchUsersFunc: PropTypes.func.isRequired,
|
||||
searchUsersFunc: PropTypes.func,
|
||||
searchGroupsFunc: PropTypes.func
|
||||
};
|
||||
|
||||
@ -66,29 +66,39 @@ class LogUserSelector extends Component {
|
||||
this.setState({
|
||||
isLoading: true
|
||||
});
|
||||
|
||||
this.props.searchUsersFunc(value).then((res) => {
|
||||
const users = res.data.user_list || res.data.users || [];
|
||||
this.setState({
|
||||
searchResults: users,
|
||||
isLoading: false
|
||||
}, () => {
|
||||
if (this.props.searchGroupsFunc) {
|
||||
this.props.searchGroupsFunc(value).then((res) => {
|
||||
const groups = res.data.group_list || res.data.groups || [];
|
||||
this.setState({
|
||||
searchResults: [...users, ...groups]
|
||||
if (this.props.searchUsersFunc) {
|
||||
this.props.searchUsersFunc(value).then((res) => {
|
||||
const users = res.data.user_list || res.data.users || [];
|
||||
this.setState({
|
||||
searchResults: users,
|
||||
isLoading: false
|
||||
}, () => {
|
||||
if (this.props.searchGroupsFunc) {
|
||||
this.props.searchGroupsFunc(value).then((res) => {
|
||||
const groups = res.data.group_list || res.data.groups || [];
|
||||
this.setState({
|
||||
searchResults: [...users, ...groups]
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
isLoading: false
|
||||
});
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
isLoading: false
|
||||
}
|
||||
if (this.props.searchGroupsFunc) {
|
||||
this.props.searchGroupsFunc(value).then((res) => {
|
||||
const groups = res.data.group_list || res.data.groups || [];
|
||||
this.setState({
|
||||
searchResults: groups,
|
||||
isLoading: false
|
||||
});
|
||||
});
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
toggleSelectItem = (e, item) => {
|
||||
|
@ -3,15 +3,16 @@ import PropTypes from 'prop-types';
|
||||
import { Link } from '@gatsbyjs/reach-router';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { seafileAPI } from '../../../utils/seafile-api';
|
||||
import { gettext, siteRoot } from '../../../utils/constants';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { systemAdminAPI } from '../../../utils/system-admin-api';
|
||||
import EmptyTip from '../../../components/empty-tip';
|
||||
import Loading from '../../../components/loading';
|
||||
import Paginator from '../../../components/paginator';
|
||||
import MainPanelTopbar from '../main-panel-topbar';
|
||||
import UserLink from '../user-link';
|
||||
import LogsNav from './logs-nav';
|
||||
import LogUserSelector from '../../dashboard/log-user-selector';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@ -154,6 +155,11 @@ class GroupMemberAuditLogs extends Component {
|
||||
perPage: 100,
|
||||
currentPage: 1,
|
||||
hasNextPage: false,
|
||||
availableUsers: [],
|
||||
selectedUsers: [],
|
||||
selectedOperators: [],
|
||||
selectedGroups: [],
|
||||
openSelector: null,
|
||||
};
|
||||
this.initPage = 1;
|
||||
}
|
||||
@ -170,8 +176,15 @@ class GroupMemberAuditLogs extends Component {
|
||||
}
|
||||
|
||||
getLogsByPage = (page) => {
|
||||
let { perPage } = this.state;
|
||||
seafileAPI.sysAdminListGroupInviteLogs(page, perPage).then((res) => {
|
||||
let { perPage, selectedUsers, selectedOperators, selectedGroups } = this.state;
|
||||
|
||||
const emails = {
|
||||
user_emails: selectedUsers.map(user => user.email),
|
||||
operator_emails: selectedOperators.map(user => user.email),
|
||||
group_ids: selectedGroups.map(group => group.id)
|
||||
};
|
||||
|
||||
systemAdminAPI.sysAdminListGroupInviteLogs(page, perPage, emails).then((res) => {
|
||||
this.setState({
|
||||
logList: res.data.group_invite_log_list,
|
||||
loading: false,
|
||||
@ -186,6 +199,102 @@ class GroupMemberAuditLogs extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
handleUserFilter = (user, shouldFetchData = true) => {
|
||||
const { selectedUsers } = this.state;
|
||||
let newSelectedUsers;
|
||||
|
||||
if (user === null) {
|
||||
newSelectedUsers = selectedUsers;
|
||||
} else {
|
||||
const isSelected = selectedUsers.find(item => item.email === user.email);
|
||||
if (isSelected) {
|
||||
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
|
||||
} else {
|
||||
newSelectedUsers = [...selectedUsers, user];
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedUsers: newSelectedUsers,
|
||||
currentPage: 1
|
||||
}, () => {
|
||||
if (shouldFetchData) {
|
||||
this.getLogsByPage(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleOperatorFilter = (user, shouldFetchData = true) => {
|
||||
const { selectedOperators } = this.state;
|
||||
let newSelectedUsers;
|
||||
|
||||
if (user === null) {
|
||||
newSelectedUsers = selectedOperators;
|
||||
} else {
|
||||
const isSelected = selectedOperators.find(item => item.email === user.email);
|
||||
if (isSelected) {
|
||||
newSelectedUsers = selectedOperators.filter(item => item.email !== user.email);
|
||||
} else {
|
||||
newSelectedUsers = [...selectedOperators, user];
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedOperators: newSelectedUsers,
|
||||
currentPage: 1
|
||||
}, () => {
|
||||
if (shouldFetchData) {
|
||||
this.getLogsByPage(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleGroupFilter = (group, shouldFetchData = true) => {
|
||||
const { selectedGroups } = this.state;
|
||||
let newSelectedGroups;
|
||||
|
||||
if (group === null) {
|
||||
newSelectedGroups = selectedGroups;
|
||||
} else {
|
||||
const isSelected = selectedGroups.find(item => item.id === group.id);
|
||||
if (isSelected) {
|
||||
newSelectedGroups = selectedGroups.filter(item => item.id !== group.id);
|
||||
} else {
|
||||
newSelectedGroups = [...selectedGroups, group];
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedGroups: newSelectedGroups,
|
||||
currentPage: 1
|
||||
}, () => {
|
||||
if (shouldFetchData) {
|
||||
this.getLogsByPage(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleSelectorToggle = (selectorType) => {
|
||||
const { openSelector } = this.state;
|
||||
const wasOpen = openSelector === selectorType;
|
||||
|
||||
this.setState({
|
||||
openSelector: wasOpen ? null : selectorType
|
||||
}, () => {
|
||||
if (wasOpen) {
|
||||
this.getLogsByPage(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
searchUsers = (value) => {
|
||||
return systemAdminAPI.sysAdminSearchUsers(value);
|
||||
};
|
||||
|
||||
searchGroups = (value) => {
|
||||
return systemAdminAPI.sysAdminSearchGroups(value);
|
||||
};
|
||||
|
||||
resetPerPage = (newPerPage) => {
|
||||
this.setState({
|
||||
perPage: newPerPage,
|
||||
@ -193,7 +302,12 @@ class GroupMemberAuditLogs extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
let { logList, currentPage, perPage, hasNextPage } = this.state;
|
||||
let {
|
||||
logList, currentPage, perPage, hasNextPage,
|
||||
availableUsers, selectedUsers, selectedOperators, selectedGroups,
|
||||
openSelector
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<MainPanelTopbar {...this.props} />
|
||||
@ -201,16 +315,47 @@ class GroupMemberAuditLogs extends Component {
|
||||
<div className="cur-view-container">
|
||||
<LogsNav currentItem="groupMember" />
|
||||
<div className="cur-view-content">
|
||||
<Content
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={logList}
|
||||
currentPage={currentPage}
|
||||
perPage={perPage}
|
||||
hasNextPage={hasNextPage}
|
||||
getLogsByPage={this.getLogsByPage}
|
||||
resetPerPage={this.resetPerPage}
|
||||
/>
|
||||
<Fragment>
|
||||
<div className="d-flex align-items-center mb-2">
|
||||
<LogUserSelector
|
||||
componentName="Member"
|
||||
items={availableUsers}
|
||||
selectedItems={selectedUsers}
|
||||
onSelect={this.handleUserFilter}
|
||||
isOpen={openSelector === 'user'}
|
||||
onToggle={() => this.handleSelectorToggle('user')}
|
||||
searchUsersFunc={this.searchUsers}
|
||||
/>
|
||||
<LogUserSelector
|
||||
componentName="Group"
|
||||
items={availableUsers}
|
||||
selectedItems={selectedGroups}
|
||||
onSelect={this.handleGroupFilter}
|
||||
isOpen={openSelector === 'group'}
|
||||
onToggle={() => this.handleSelectorToggle('group')}
|
||||
searchGroupsFunc={this.searchGroups}
|
||||
/>
|
||||
<LogUserSelector
|
||||
componentName="Operator"
|
||||
items={availableUsers}
|
||||
selectedItems={selectedOperators}
|
||||
onSelect={this.handleOperatorFilter}
|
||||
isOpen={openSelector === 'operator'}
|
||||
onToggle={() => this.handleSelectorToggle('operator')}
|
||||
searchUsersFunc={this.searchUsers}
|
||||
/>
|
||||
</div>
|
||||
<Content
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={logList}
|
||||
currentPage={currentPage}
|
||||
perPage={perPage}
|
||||
hasNextPage={hasNextPage}
|
||||
getLogsByPage={this.getLogsByPage}
|
||||
resetPerPage={this.resetPerPage}
|
||||
/>
|
||||
</Fragment>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2235,14 +2235,6 @@ class SeafileAPI {
|
||||
return this.req.get(url, { params: params });
|
||||
}
|
||||
|
||||
sysAdminListGroupInviteLogs(page, perPage) {
|
||||
const url = this.server + '/api/v2.1/admin/logs/group-member-audit/';
|
||||
let params = {
|
||||
page: page,
|
||||
per_page: perPage
|
||||
};
|
||||
return this.req.get(url, { params: params });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -770,6 +770,24 @@ class SystemAdminAPI {
|
||||
return this.req.get(url, { params: params });
|
||||
}
|
||||
|
||||
sysAdminListGroupInviteLogs(page, perPage, emails) {
|
||||
const url = this.server + '/api/v2.1/admin/logs/group-member-audit/';
|
||||
let params = {
|
||||
page: page,
|
||||
per_page: perPage,
|
||||
};
|
||||
if (emails.user_emails && emails.user_emails.length) {
|
||||
params.user_emails = emails.user_emails.join(',');
|
||||
}
|
||||
if (emails.operator_emails && emails.operator_emails.length) {
|
||||
params.operator_emails = emails.operator_emails.join(',');
|
||||
}
|
||||
if (emails.group_ids && emails.group_ids.length) {
|
||||
params.group_ids = emails.group_ids.join(',');
|
||||
}
|
||||
return this.req.get(url, { params: params });
|
||||
}
|
||||
|
||||
sysAdminExportLogsExcel(start, end, logType) {
|
||||
const url = this.server + '/api/v2.1/admin/logs/export-excel/';
|
||||
const params = {
|
||||
|
@ -660,7 +660,30 @@ class AdminLogGroupMemberAuditLogs(APIView):
|
||||
error_msg = 'limit invalid'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
events = GroupMemberAudit.objects.all().order_by('-timestamp')[start:start+limit+1]
|
||||
user_emails = request.GET.get('user_emails')
|
||||
user_emails = user_emails.split(',') if user_emails else []
|
||||
for user_selected in user_emails:
|
||||
if not is_valid_email(user_selected):
|
||||
error_msg = 'email %s invalid.' % user_selected
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
operator_emails = request.GET.get('operator_emails')
|
||||
operator_emails = operator_emails.split(',') if operator_emails else []
|
||||
for user_selected in operator_emails:
|
||||
if not is_valid_email(user_selected):
|
||||
error_msg = 'email %s invalid.' % user_selected
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
group_ids = request.GET.get('group_ids')
|
||||
group_ids = group_ids.split(',') if group_ids else []
|
||||
|
||||
queryset = GroupMemberAudit.objects.all()
|
||||
if user_emails:
|
||||
queryset = queryset.by_users(user_emails)
|
||||
if operator_emails:
|
||||
queryset = queryset.by_operators(operator_emails)
|
||||
if group_ids:
|
||||
queryset = queryset.by_group_ids(group_ids)
|
||||
|
||||
events = queryset.order_by('-timestamp')[start:start+limit+1]
|
||||
if len(events) > limit:
|
||||
has_next_page = True
|
||||
events = events[:limit]
|
||||
|
@ -497,6 +497,16 @@ class RepoTransfer(models.Model):
|
||||
GROUP_MEMBER_ADD = 'group_member_add'
|
||||
GROUP_MEMBER_DELETE = 'group_member_delete'
|
||||
|
||||
class GroupMemberAuditQuerySet(models.QuerySet):
|
||||
def by_users(self, users):
|
||||
return self.filter(user__in=users)
|
||||
|
||||
def by_operators(self, operators):
|
||||
return self.filter(operator__in=operators)
|
||||
|
||||
def by_group_ids(self, group_ids):
|
||||
return self.filter(group_id__in=group_ids)
|
||||
|
||||
class GroupMemberAudit(models.Model):
|
||||
org_id = models.IntegerField(db_index=True)
|
||||
group_id = models.IntegerField(db_index=True)
|
||||
@ -504,6 +514,7 @@ class GroupMemberAudit(models.Model):
|
||||
operator = models.CharField(max_length=255, db_index=True)
|
||||
operation = models.CharField(max_length=128)
|
||||
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
|
||||
objects = GroupMemberAuditQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
db_table = 'group_member_audit'
|
||||
|
Loading…
Reference in New Issue
Block a user