1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-04-28 03:10:45 +00:00

admin filter completed

This commit is contained in:
孙永强 2025-03-24 12:06:20 +08:00
parent 93c09294b7
commit b161fac6f8
9 changed files with 359 additions and 81 deletions

View File

@ -83,10 +83,10 @@ class LogUserSelector extends Component {
<span className="cur-activity-modifiers d-inline-block p-2 rounded" onClick={this.onToggleClick}>
{selectedItems.length > 0 ? (
<>
<span>{gettext('Users:')}</span>
<span>{gettext(this.props.componentName + ":")}</span>
<span className="d-inline-block ml-1">{selectedItems.map(item => item.name).join(', ')}</span>
</>
) : gettext('Users')}
) : gettext(this.props.componentName)}
<i className="sf3-font sf3-font-down ml-2 toggle-icon"></i>
</span>
{isOpen && (
@ -143,6 +143,7 @@ class LogUserSelector extends Component {
}
LogUserSelector.propTypes = {
componentName: PropTypes.string,
items: PropTypes.array.isRequired,
selectedItems: PropTypes.array.isRequired,
onSelect: PropTypes.func.isRequired,

View File

@ -48,6 +48,7 @@ class Content extends Component {
loading, errorMsg, items,
perPage, currentPage, hasNextPage
} = this.props;
console.log(currentPage)
if (loading) {
return <Loading />;
} else if (errorMsg) {
@ -328,7 +329,7 @@ class FileAccessLogs extends Component {
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage();
this.getLogsByPage(1);
}
});
};
@ -355,6 +356,7 @@ class FileAccessLogs extends Component {
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Users"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}

View File

@ -13,6 +13,7 @@ import MainPanelTopbar from '../main-panel-topbar';
import UserLink from '../user-link';
import LogsNav from './logs-nav';
import LogUserSelector from '../../dashboard/log-user-selector';
import LogRepoSelector from '../../dashboard/log-repo-selector';
dayjs.extend(relativeTime);
@ -164,7 +165,12 @@ class FIleTransferLogs extends Component {
currentPage: 1,
hasNextPage: false,
availableUsers: [],
selectedUsers: [],
selectedFromUsers: [],
selectedToUsers: [],
selectedOperators: [],
openSelector: null,
availableRepos: [],
selectedRepos: [],
};
this.initPage = 1;
}
@ -181,9 +187,19 @@ class FIleTransferLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage, selectedUsers } = this.state;
let emails = selectedUsers.map(user => user.email);
systemAdminAPI.sysAdminListFileTransferLogs(page, perPage, emails).then((res) => {
let { perPage, selectedFromUsers, selectedToUsers, selectedOperators, selectedRepos } = this.state;
const emails = {
from_emails: selectedFromUsers.map(user => user.email),
to_emails: selectedToUsers.map(user => user.email),
operator_emails: selectedOperators.map(user => user.email)
};
systemAdminAPI.sysAdminListFileTransferLogs(
page,
perPage,
emails,
selectedRepos
).then((res) => {
this.setState({
logList: res.data.repo_transfer_log_list,
loading: false,
@ -193,7 +209,8 @@ class FIleTransferLogs extends Component {
}).catch((error) => {
this.setState({
loading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
currentPage: page,
errorMsg: Utils.getErrorMsg(error, true)
});
});
};
@ -204,23 +221,123 @@ class FIleTransferLogs extends Component {
}, () => this.getLogsByPage(this.initPage));
};
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
handleFromUserFilter = (user, shouldFetchData = true) => {
const { selectedFromUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedUsers;
newSelectedUsers = selectedFromUsers;
} else {
const isSelected = selectedUsers.find(item => item.email === user.email);
const isSelected = selectedFromUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
newSelectedUsers = selectedFromUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedUsers, user];
newSelectedUsers = [...selectedFromUsers, user];
}
}
this.setState({
selectedUsers: newSelectedUsers,
selectedFromUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleToUserFilter = (user, shouldFetchData = true) => {
const { selectedToUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedToUsers;
} else {
const isSelected = selectedToUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedToUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedToUsers, user];
}
}
this.setState({
selectedToUsers: 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);
}
});
};
handleSelectorToggle = (selectorType) => {
const { openSelector } = this.state;
const wasOpen = openSelector === selectorType;
this.setState({
openSelector: wasOpen ? null : selectorType
}, () => {
if (wasOpen) {
this.getLogsByPage(1);
}
});
};
getAvailableRepos = () => {
systemAdminAPI.sysAdminListRepos().then((res) => {
this.setState({
availableRepos: res.data.repos
});
}).catch((error) => {
this.setState({
errorMsg: Utils.getErrorMsg(error, true)
});
});
};
handleRepoFilter = (repo, shouldFetchData = true) => {
const { selectedRepos } = this.state;
let newSelectedRepos;
if (repo === null) {
newSelectedRepos = [];
} else {
const isSelected = selectedRepos.find(item => item.id === repo.id);
if (isSelected) {
newSelectedRepos = selectedRepos.filter(item => item.id !== repo.id);
} else {
newSelectedRepos = [...selectedRepos, repo];
}
}
this.setState({
selectedRepos: newSelectedRepos,
currentPage: 1
}, () => {
if (shouldFetchData) {
@ -230,7 +347,13 @@ class FIleTransferLogs extends Component {
};
render() {
let { logList, currentPage, perPage, hasNextPage, availableUsers, selectedUsers } = this.state;
let {
logList, currentPage, perPage, hasNextPage,
availableUsers, selectedFromUsers, selectedToUsers, selectedOperators,
availableRepos, selectedRepos,
openSelector
} = this.state;
return (
<Fragment>
<MainPanelTopbar {...this.props} />
@ -239,12 +362,40 @@ class FIleTransferLogs extends Component {
<LogsNav currentItem="fileTransfer" />
<div className="cur-view-content">
<Fragment>
<LogUserSelector
label={gettext('User')}
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}
/>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Transfer From"
items={availableUsers}
selectedItems={selectedFromUsers}
onSelect={this.handleFromUserFilter}
isOpen={openSelector === 'fromUser'}
onToggle={() => this.handleSelectorToggle('fromUser')}
/>
<LogUserSelector
componentName="Transfer To"
items={availableUsers}
selectedItems={selectedToUsers}
onSelect={this.handleToUserFilter}
isOpen={openSelector === 'toUser'}
onToggle={() => this.handleSelectorToggle('toUser')}
/>
<LogUserSelector
componentName="Operator"
items={availableUsers}
selectedItems={selectedOperators}
onSelect={this.handleOperatorFilter}
isOpen={openSelector === 'operator'}
onToggle={() => this.handleSelectorToggle('operator')}
/>
<div className="mx-3"></div>
<LogRepoSelector
items={availableRepos}
selectedItems={selectedRepos}
onSelect={this.handleRepoFilter}
isOpen={openSelector === 'repo'}
onToggle={() => this.handleSelectorToggle('repo')}
/>
</div>
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}

View File

@ -325,6 +325,7 @@ class FileUpdateLogs extends Component {
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Users"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}

View File

@ -244,6 +244,7 @@ class LoginLogs extends Component {
<div className="cur-view-content">
<Fragment>
<LogUserSelector
componentName="Users"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}

View File

@ -154,7 +154,8 @@ class SharePermissionLogs extends Component {
hasNextPage: false,
isExportExcelDialogOpen: false,
availableUsers: [],
selectedUsers: [],
selectedFromUsers: [],
selectedToUsers: [],
availableRepos: [],
selectedRepos: [],
openSelector: null,
@ -178,9 +179,20 @@ class SharePermissionLogs extends Component {
}
getLogsByPage = (page) => {
let { perPage, selectedUsers, selectedRepos } = this.state;
let emails = selectedUsers.map(user => user.email);
systemAdminAPI.sysAdminListSharePermissionLogs(page, perPage, emails, selectedRepos).then((res) => {
let { perPage, selectedFromUsers, selectedToUsers, selectedRepos } = this.state;
let fromEmails = selectedFromUsers.map(user => user.email);
let toEmails = selectedToUsers.map(user => user.email);
const emails = {
from_emails: fromEmails,
to_emails: toEmails
}
console.log(emails)
systemAdminAPI.sysAdminListSharePermissionLogs(
page,
perPage,
emails,
selectedRepos
).then((res) => {
this.setState({
logList: res.data.share_permission_log_list,
loading: false,
@ -190,7 +202,7 @@ class SharePermissionLogs extends Component {
}).catch((error) => {
this.setState({
loading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
errorMsg: Utils.getErrorMsg(error, true)
});
});
};
@ -201,23 +213,48 @@ class SharePermissionLogs extends Component {
}, () => this.getLogsByPage(this.initPage));
};
handleUserFilter = (user, shouldFetchData = true) => {
const { selectedUsers } = this.state;
handleFromUserFilter = (user, shouldFetchData = true) => {
const { selectedFromUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedUsers;
newSelectedUsers = selectedFromUsers;
} else {
const isSelected = selectedUsers.find(item => item.email === user.email);
const isSelected = selectedFromUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedUsers.filter(item => item.email !== user.email);
newSelectedUsers = selectedFromUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedUsers, user];
newSelectedUsers = [...selectedFromUsers, user];
}
}
this.setState({
selectedUsers: newSelectedUsers,
selectedFromUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
this.getLogsByPage(1);
}
});
};
handleToUserFilter = (user, shouldFetchData = true) => {
const { selectedToUsers } = this.state;
let newSelectedUsers;
if (user === null) {
newSelectedUsers = selectedToUsers;
} else {
const isSelected = selectedToUsers.find(item => item.email === user.email);
if (isSelected) {
newSelectedUsers = selectedToUsers.filter(item => item.email !== user.email);
} else {
newSelectedUsers = [...selectedToUsers, user];
}
}
this.setState({
selectedToUsers: newSelectedUsers,
currentPage: 1
}, () => {
if (shouldFetchData) {
@ -289,7 +326,11 @@ class SharePermissionLogs extends Component {
};
render() {
let { logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen, availableUsers, selectedUsers, availableRepos, selectedRepos } = this.state;
let {
logList, currentPage, perPage, hasNextPage, isExportExcelDialogOpen,
availableUsers, selectedFromUsers, selectedToUsers,
availableRepos, selectedRepos, openSelector
} = this.state;
return (
<Fragment>
<MainPanelTopbar {...this.props}>
@ -302,18 +343,27 @@ class SharePermissionLogs extends Component {
<Fragment>
<div className="d-flex align-items-center mb-2">
<LogUserSelector
componentName="Share From"
items={availableUsers}
selectedItems={selectedUsers}
onSelect={this.handleUserFilter}
isOpen={this.state.openSelector === 'user'}
onToggle={() => this.handleSelectorToggle('user')}
selectedItems={selectedFromUsers}
onSelect={this.handleFromUserFilter}
isOpen={openSelector === 'fromUser'}
onToggle={() => this.handleSelectorToggle('fromUser')}
/>
<LogUserSelector
componentName="Share To"
items={availableUsers}
selectedItems={selectedToUsers}
onSelect={this.handleToUserFilter}
isOpen={openSelector === 'toUser'}
onToggle={() => this.handleSelectorToggle('toUser')}
/>
<div className="mx-3"></div>
<LogRepoSelector
items={availableRepos}
selectedItems={selectedRepos}
onSelect={this.handleRepoFilter}
isOpen={this.state.openSelector === 'repo'}
isOpen={openSelector === 'repo'}
onToggle={() => this.handleSelectorToggle('repo')}
/>
</div>

View File

@ -728,8 +728,11 @@ class SystemAdminAPI {
page: page,
per_page: perPage
};
if (emails && emails.length) {
params.emails = emails.join(',');
if (emails.from_emails && emails.from_emails.length) {
params.from_emails = emails.from_emails.join(',')
}
if (emails.to_emails && emails.to_emails.length) {
params.to_emails = emails.to_emails.join(',')
}
if (repos && repos.length) {
params.repos = repos.map(repo => repo.id).join(',');
@ -737,17 +740,23 @@ class SystemAdminAPI {
return this.req.get(url, { params: params });
}
sysAdminListFileTransferLogs(page, perPage, emails, repoID) {
sysAdminListFileTransferLogs(page, perPage, emails, repos) {
const url = this.server + '/api/v2.1/admin/logs/repo-transfer-logs/';
let params = {
page: page,
per_page: perPage
};
if (emails && emails.length) {
params.emails = emails.join(',');
if (emails.from_emails && emails.from_emails.length) {
params.from_emails = emails.from_emails.join(',');
}
if (repoID != undefined) {
params.repo_id = repoID;
if (emails.to_emails && emails.to_emails.length) {
params.to_emails = emails.to_emails.join(',');
}
if (emails.operator_emails && emails.operator_emails.length) {
params.operator_emails = emails.operator_emails.join(',');
}
if (repos && repos.length) {
params.repos = repos.map(repo => repo.id).join(',');
}
return this.req.get(url, { params: params });
}

View File

@ -120,18 +120,20 @@ class AdminLogsFileAccessLogs(APIView):
if not is_valid_email(user_selected):
error_msg = 'email %s invalid.' % user_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repos = request.GET.get('repos')
repos = repos.split(',') if repos else []
for repo_selected in repos:
if not is_valid_repo_id_format(repo_selected):
error_msg = 'repo_id %s invalid.' % repo_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repos = request.GET.get('repos', None)
if repos:
repos = repos.split(',') if repos else []
for repo_selected in repos:
if not is_valid_repo_id_format(repo_selected):
error_msg = 'repo_id %s invalid.' % repo_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
start = per_page * (current_page - 1)
limit = per_page + 1
events = get_log_events_by_type_users_repo('file_audit', emails, repos, start, limit) or []
if events is None:
events = []
if len(events) > per_page:
events = events[:per_page]
has_next_page = True
@ -219,16 +221,20 @@ class AdminLogsFileUpdateLogs(APIView):
error_msg = 'email %s invalid.' % user_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repo_id_selected = request.GET.get('repo_id', None)
if repo_id_selected and not is_valid_repo_id_format(repo_id_selected):
error_msg = 'repo_id %s invalid.' % repo_id_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repos = request.GET.get('repos', None)
if repos:
repos = repos.split(',')
for repo_selected in repos:
if not is_valid_repo_id_format(repo_selected):
error_msg = 'repo_id %s invalid.' % repo_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
start = per_page * (current_page - 1)
limit = per_page
events = get_log_events_by_type_users_repo('file_update', emails, repo_id_selected, start, limit)
events = get_log_events_by_type_users_repo('file_update', emails, repos, start, limit)
if events is None:
events = []
has_next_page = True if len(events) == per_page else False
# Use dict to reduce memcache fetch cost in large for-loop.
@ -299,22 +305,40 @@ class AdminLogsSharePermissionLogs(APIView):
current_page = 1
per_page = 100
emails = request.GET.get('emails')
emails = emails.split(',') if emails else []
for user_selected in emails:
from_emails = request.GET.get('from_emails')
from_emails = from_emails.split(',') if from_emails else []
for user_selected in from_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)
repo_id_selected = request.GET.get('repo_id', None)
if repo_id_selected and not is_valid_repo_id_format(repo_id_selected):
error_msg = 'repo_id %s invalid.' % repo_id_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
to_emails = request.GET.get('to_emails')
to_emails = to_emails.split(',') if to_emails else []
for user_selected in to_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)
emails = {
'from_emails': from_emails,
'to_emails': to_emails
}
repos = request.GET.get('repos', None)
if repos:
repos = repos.split(',')
for repo_selected in repos:
if not is_valid_repo_id_format(repo_selected):
error_msg = 'repo_id %s invalid.' % repo_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
start = per_page * (current_page - 1)
limit = per_page
events = get_log_events_by_type_users_repo('perm_audit', emails, repo_id_selected, start, limit) or []
events = get_log_events_by_type_users_repo('perm_audit', emails, repos, start, limit) or []
if events is None:
events = []
has_next_page = True if len(events) == per_page else False
# Use dict to reduce memcache fetch cost in large for-loop.
@ -454,21 +478,45 @@ class AdminLogsFileTransferLogs(APIView):
error_msg = 'limit invalid'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
emails = request.GET.get('emails')
emails = emails.split(',') if emails else []
for user_selected in emails:
from_emails = request.GET.get('from_emails')
from_emails = from_emails.split(',') if from_emails else []
for user_selected in from_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)
if emails:
events = RepoTransfer.objects.filter(
Q(from_user__in=emails) |
Q(to__in=emails) |
Q(operator__in=emails)
).order_by('-timestamp')[start:start+limit+1]
else:
events = RepoTransfer.objects.all().order_by('-timestamp')[start:start+limit+1]
if len(events) > limit:
to_emails = request.GET.get('to_emails')
to_emails = to_emails.split(',') if to_emails else []
for user_selected in to_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)
repos = request.GET.get('repos', None)
if repos:
repos = repos.split(',')
for repo_selected in repos:
if not is_valid_repo_id_format(repo_selected):
error_msg = 'repo_id %s invalid.' % repo_selected
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
queryset = RepoTransfer.objects.all()
if from_emails:
queryset = queryset.by_from_user(from_emails)
if to_emails:
queryset = queryset.by_to_user(to_emails)
if operator_emails:
queryset = queryset.by_operator(operator_emails)
if repos:
queryset = queryset.by_repo_ids(repos)
events = queryset.order_by('-timestamp')[start:start+limit+1]
if events and len(events) > limit:
has_next_page = True
events = events[:limit]
else:

View File

@ -466,6 +466,20 @@ class ClientSSOToken(models.Model):
return super(ClientSSOToken, self).save(*args, **kwargs)
class RepoTransferQuerySet(models.QuerySet):
def by_from_user(self, from_users):
return self.filter(from_user__in=from_users)
def by_to_user(self, to_users):
return self.filter(to__in=to_users)
def by_operator(self, operators):
return self.filter(operator__in=operators)
def by_repo_ids(self, repo_ids):
return self.filter(repo_id__in=repo_ids)
class RepoTransfer(models.Model):
repo_id = models.CharField(max_length=36)
org_id = models.IntegerField(db_index=True)
@ -473,6 +487,7 @@ class RepoTransfer(models.Model):
to = models.CharField(max_length=255)
operator = models.CharField(max_length=255)
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
objects = RepoTransferQuerySet.as_manager()
class Meta:
db_table = 'RepoTransfer'