2019-10-22 11:49:30 +00:00
|
|
|
import React, { Component, Fragment } from 'react';
|
|
|
|
import { seafileAPI } from '../../../utils/seafile-api';
|
2020-12-04 06:18:34 +00:00
|
|
|
import { gettext } from '../../../utils/constants';
|
2019-12-05 07:45:16 +00:00
|
|
|
import { Utils } from '../../../utils/utils';
|
2019-10-22 11:49:30 +00:00
|
|
|
import EmptyTip from '../../../components/empty-tip';
|
|
|
|
import { Button } from 'reactstrap';
|
2022-12-29 04:21:47 +00:00
|
|
|
import { navigate } from '@gatsbyjs/reach-router';
|
2019-10-22 11:49:30 +00:00
|
|
|
import moment from 'moment';
|
|
|
|
import Loading from '../../../components/loading';
|
|
|
|
import Paginator from '../../../components/paginator';
|
2020-12-04 06:18:34 +00:00
|
|
|
import LogsExportExcelDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-logs-export-excel-dialog';
|
|
|
|
import ModalPortal from '../../../components/modal-portal';
|
2019-10-22 11:49:30 +00:00
|
|
|
import LogsNav from './logs-nav';
|
2020-12-04 06:18:34 +00:00
|
|
|
import FilterMenu from './file-access-item-menu';
|
|
|
|
import ToggleFilter from './file-access-toggle-filter';
|
2019-10-22 11:49:30 +00:00
|
|
|
import MainPanelTopbar from '../main-panel-topbar';
|
2019-11-07 06:17:59 +00:00
|
|
|
import UserLink from '../user-link';
|
2019-10-22 11:49:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Content extends Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2020-12-04 06:18:34 +00:00
|
|
|
this.state = {
|
|
|
|
isItemFreezed: false
|
|
|
|
};
|
2019-10-22 11:49:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getPreviousPage = () => {
|
|
|
|
this.props.getLogsByPage(this.props.currentPage - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
getNextPage = () => {
|
|
|
|
this.props.getLogsByPage(this.props.currentPage + 1);
|
|
|
|
}
|
|
|
|
|
2020-12-04 06:18:34 +00:00
|
|
|
toggleFilterByUser = () => {
|
|
|
|
this.props.filterByUser(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleFilterByRepo = () => {
|
|
|
|
this.props.filterByRepo(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleFreezeItem = (freezed) => {
|
|
|
|
this.setState({
|
|
|
|
isItemFreezed: freezed
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-10-22 11:49:30 +00:00
|
|
|
render() {
|
2020-12-04 06:18:34 +00:00
|
|
|
const {
|
|
|
|
loading, errorMsg, items,
|
|
|
|
userFilteredBy, repoFilteredBy,
|
|
|
|
perPage, currentPage, hasNextPage
|
|
|
|
} = this.props;
|
2019-10-22 11:49:30 +00:00
|
|
|
if (loading) {
|
|
|
|
return <Loading />;
|
|
|
|
} else if (errorMsg) {
|
|
|
|
return <p className="error text-center">{errorMsg}</p>;
|
|
|
|
} else {
|
|
|
|
const emptyTip = (
|
|
|
|
<EmptyTip>
|
2020-03-06 08:21:45 +00:00
|
|
|
<h2>{gettext('No file access logs')}</h2>
|
2019-10-22 11:49:30 +00:00
|
|
|
</EmptyTip>
|
|
|
|
);
|
|
|
|
const table = (
|
|
|
|
<Fragment>
|
2020-12-04 06:18:34 +00:00
|
|
|
<div>
|
|
|
|
{userFilteredBy && (
|
|
|
|
<ToggleFilter
|
|
|
|
filterBy={items[0].name}
|
|
|
|
toggleFilter={this.toggleFilterByUser}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{repoFilteredBy && (
|
|
|
|
<ToggleFilter
|
|
|
|
filterBy={items[0].repo_name}
|
|
|
|
toggleFilter={this.toggleFilterByRepo}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<table>
|
2019-10-22 11:49:30 +00:00
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th width="20%">{gettext('Name')}</th>
|
|
|
|
<th width="10%">{gettext('Type')}</th>
|
|
|
|
<th width="20%">{gettext('IP')}{' / '}{gettext('Device')}</th>
|
|
|
|
<th width="20%">{gettext('Date')}</th>
|
|
|
|
<th width="15%">{gettext('Library')}</th>
|
|
|
|
<th width="15%">{gettext('File')}{' / '}{gettext('Folder')}</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
2020-11-02 05:56:35 +00:00
|
|
|
{items &&
|
2019-10-22 11:49:30 +00:00
|
|
|
<tbody>
|
|
|
|
{items.map((item, index) => {
|
|
|
|
return (<Item
|
|
|
|
key={index}
|
|
|
|
item={item}
|
2020-12-04 06:18:34 +00:00
|
|
|
isFreezed={this.state.isItemFreezed}
|
|
|
|
toggleFreezeItem={this.toggleFreezeItem}
|
|
|
|
userFilteredBy={userFilteredBy}
|
|
|
|
repoFilteredBy={repoFilteredBy}
|
|
|
|
filterByUser={this.props.filterByUser}
|
|
|
|
filterByRepo={this.props.filterByRepo}
|
2019-10-22 11:49:30 +00:00
|
|
|
/>);
|
|
|
|
})}
|
|
|
|
</tbody>
|
|
|
|
}
|
|
|
|
</table>
|
|
|
|
<Paginator
|
|
|
|
gotoPreviousPage={this.getPreviousPage}
|
|
|
|
gotoNextPage={this.getNextPage}
|
|
|
|
currentPage={currentPage}
|
|
|
|
hasNextPage={hasNextPage}
|
|
|
|
curPerPage={perPage}
|
|
|
|
resetPerPage={this.props.resetPerPage}
|
|
|
|
/>
|
|
|
|
</Fragment>
|
|
|
|
);
|
2020-11-02 05:56:35 +00:00
|
|
|
return items.length ? table : emptyTip;
|
2019-10-22 11:49:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Item extends Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
2020-12-04 06:18:34 +00:00
|
|
|
isHighlighted: false,
|
|
|
|
isOpIconShown: false
|
2019-10-22 11:49:30 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-04 06:18:34 +00:00
|
|
|
handleMouseEnter = () => {
|
|
|
|
if (!this.props.isFreezed) {
|
|
|
|
this.setState({
|
|
|
|
isHighlighted: true,
|
|
|
|
isOpIconShown: true
|
|
|
|
});
|
|
|
|
}
|
2019-10-22 11:49:30 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 06:18:34 +00:00
|
|
|
handleMouseLeave = () => {
|
|
|
|
if (!this.props.isFreezed) {
|
|
|
|
this.setState({
|
|
|
|
isHighlighted: false,
|
|
|
|
isOpIconShown: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
filterByUser = () => {
|
|
|
|
const { item } = this.props;
|
|
|
|
this.props.filterByUser(item.email);
|
|
|
|
}
|
|
|
|
|
|
|
|
filterByRepo = () => {
|
|
|
|
const { item } = this.props;
|
|
|
|
this.props.filterByRepo(item.repo_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleFreezeItem = (freezed) => {
|
|
|
|
this.props.toggleFreezeItem(freezed);
|
|
|
|
if (!freezed) {
|
|
|
|
this.setState({
|
|
|
|
isHighlighted: false,
|
|
|
|
isOpIconShown: false
|
|
|
|
});
|
|
|
|
}
|
2019-10-22 11:49:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2020-12-04 06:18:34 +00:00
|
|
|
const { isHighlighted, isOpIconShown } = this.state;
|
|
|
|
const { item, userFilteredBy, repoFilteredBy } = this.props;
|
2019-10-22 11:49:30 +00:00
|
|
|
return (
|
2020-12-04 06:18:34 +00:00
|
|
|
<tr className={isHighlighted ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
|
|
|
<td>
|
|
|
|
<UserLink email={item.email} name={item.name} />
|
|
|
|
{isOpIconShown && !userFilteredBy && (
|
|
|
|
<FilterMenu
|
|
|
|
filterBy={item.name}
|
|
|
|
filterItems={this.filterByUser}
|
|
|
|
toggleFreezeItem={this.toggleFreezeItem}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</td>
|
2019-10-22 11:49:30 +00:00
|
|
|
<td>{item.event_type}</td>
|
2020-12-04 06:18:34 +00:00
|
|
|
<td>{item.ip}{' / '}{item.device || '--'}</td>
|
2019-10-22 11:49:30 +00:00
|
|
|
<td>{moment(item.time).fromNow()}</td>
|
2020-12-04 06:18:34 +00:00
|
|
|
<td>
|
|
|
|
{item.repo_name ? item.repo_name : gettext('Deleted')}
|
|
|
|
{isOpIconShown && item.repo_name && !repoFilteredBy && (
|
|
|
|
<FilterMenu
|
|
|
|
filterBy={item.repo_name}
|
|
|
|
filterItems={this.filterByRepo}
|
|
|
|
toggleFreezeItem={this.toggleFreezeItem}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</td>
|
2019-10-22 11:49:30 +00:00
|
|
|
<td>{item.file_or_dir_name}</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class FileAccessLogs extends Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
loading: true,
|
|
|
|
errorMsg: '',
|
|
|
|
logList: [],
|
2020-01-13 04:07:24 +00:00
|
|
|
perPage: 25,
|
2019-10-22 11:49:30 +00:00
|
|
|
currentPage: 1,
|
|
|
|
hasNextPage: false,
|
|
|
|
isExportExcelDialogOpen: false,
|
|
|
|
};
|
|
|
|
this.initPage = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleExportExcelDialog = () => {
|
|
|
|
this.setState({isExportExcelDialogOpen: !this.state.isExportExcelDialogOpen});
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount () {
|
2020-01-13 04:07:24 +00:00
|
|
|
let urlParams = (new URL(window.location)).searchParams;
|
|
|
|
const { currentPage, perPage } = this.state;
|
|
|
|
this.setState({
|
|
|
|
perPage: parseInt(urlParams.get('per_page') || perPage),
|
2020-12-04 06:18:34 +00:00
|
|
|
currentPage: parseInt(urlParams.get('page') || currentPage),
|
|
|
|
userFilteredBy: urlParams.get('email'),
|
|
|
|
repoFilteredBy: urlParams.get('repo_id')
|
2020-01-13 04:07:24 +00:00
|
|
|
}, () => {
|
|
|
|
this.getLogsByPage(this.state.currentPage);
|
|
|
|
});
|
2019-10-22 11:49:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getLogsByPage = (page) => {
|
2020-12-04 06:18:34 +00:00
|
|
|
const { perPage, userFilteredBy, repoFilteredBy } = this.state;
|
|
|
|
seafileAPI.sysAdminListFileAccessLogs(page, perPage, userFilteredBy, repoFilteredBy).then((res) => {
|
2019-10-22 11:49:30 +00:00
|
|
|
this.setState({
|
|
|
|
logList: res.data.file_access_log_list,
|
|
|
|
loading: false,
|
|
|
|
currentPage: page,
|
|
|
|
hasNextPage: res.data.has_next_page,
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
2019-12-05 07:45:16 +00:00
|
|
|
this.setState({
|
|
|
|
loading: false,
|
|
|
|
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
|
|
|
});
|
2019-10-22 11:49:30 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
resetPerPage = (newPerPage) => {
|
|
|
|
this.setState({
|
|
|
|
perPage: newPerPage,
|
|
|
|
}, () => this.getLogsByPage(this.initPage));
|
|
|
|
}
|
|
|
|
|
2020-12-04 06:18:34 +00:00
|
|
|
updateURL = (obj) => {
|
|
|
|
let url = new URL(location.href);
|
|
|
|
let searchParams = new URLSearchParams(url.search);
|
|
|
|
for (let key in obj) {
|
|
|
|
obj[key] == null ?
|
|
|
|
searchParams.delete(key) :
|
|
|
|
searchParams.set(key, obj[key]);
|
|
|
|
}
|
|
|
|
url.search = searchParams.toString();
|
|
|
|
navigate(url.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
filterByUser = (email) => {
|
|
|
|
this.setState({
|
|
|
|
userFilteredBy: email
|
|
|
|
}, () => {
|
|
|
|
this.getLogsByPage(this.initPage);
|
|
|
|
this.updateURL({'email': email});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
filterByRepo = (repoID) => {
|
|
|
|
this.setState({
|
|
|
|
repoFilteredBy: repoID
|
|
|
|
}, () => {
|
|
|
|
this.getLogsByPage(this.initPage);
|
|
|
|
this.updateURL({'repo_id': repoID});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-10-22 11:49:30 +00:00
|
|
|
render() {
|
2020-12-04 06:18:34 +00:00
|
|
|
const {
|
|
|
|
logList,
|
|
|
|
userFilteredBy, repoFilteredBy,
|
|
|
|
currentPage, perPage, hasNextPage,
|
|
|
|
isExportExcelDialogOpen
|
|
|
|
} = this.state;
|
2019-10-22 11:49:30 +00:00
|
|
|
return (
|
|
|
|
<Fragment>
|
2023-01-29 10:16:00 +00:00
|
|
|
<MainPanelTopbar {...this.props}>
|
2019-10-22 11:49:30 +00:00
|
|
|
<Button className="btn btn-secondary operation-item" onClick={this.toggleExportExcelDialog}>{gettext('Export Excel')}</Button>
|
|
|
|
</MainPanelTopbar>
|
|
|
|
<div className="main-panel-center flex-row">
|
|
|
|
<div className="cur-view-container">
|
|
|
|
<LogsNav currentItem="fileAccessLogs" />
|
|
|
|
<div className="cur-view-content">
|
|
|
|
<Content
|
|
|
|
loading={this.state.loading}
|
|
|
|
errorMsg={this.state.errorMsg}
|
|
|
|
items={logList}
|
2020-12-04 06:18:34 +00:00
|
|
|
userFilteredBy={userFilteredBy}
|
|
|
|
repoFilteredBy={repoFilteredBy}
|
|
|
|
filterByUser={this.filterByUser}
|
|
|
|
filterByRepo={this.filterByRepo}
|
2019-10-22 11:49:30 +00:00
|
|
|
currentPage={currentPage}
|
|
|
|
perPage={perPage}
|
|
|
|
hasNextPage={hasNextPage}
|
|
|
|
getLogsByPage={this.getLogsByPage}
|
|
|
|
resetPerPage={this.resetPerPage}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{isExportExcelDialogOpen &&
|
|
|
|
<ModalPortal>
|
|
|
|
<LogsExportExcelDialog
|
|
|
|
logType={'fileAccess'}
|
|
|
|
toggle={this.toggleExportExcelDialog}
|
|
|
|
/>
|
|
|
|
</ModalPortal>
|
|
|
|
}
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default FileAccessLogs;
|