1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-04 16:31:13 +00:00

repo_files_auto_del (#4626)

* repo_files_auto_del

* repo_files_auto_del

* update code
This commit is contained in:
Leo
2020-09-07 12:03:05 +08:00
committed by GitHub
parent 212e667206
commit d5f578318f
10 changed files with 351 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Form, FormGroup, Label, Input, Alert } from 'reactstrap';
import { gettext, enableRepoHistorySetting } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api.js';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
const propTypes = {
toggleDialog: PropTypes.func.isRequired,
repoID: PropTypes.string.isRequired,
};
class LibOldFilesAutoDelDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
autoDelDays: 0,
isAutoDel: false,
errorInfo: '',
};
}
componentDidMount() {
seafileAPI.getRepoOldFilesAutoDelDays(this.props.repoID).then(res => {
this.setState({
autoDelDays: res.data.auto_delete_days,
isAutoDel: res.data.auto_delete_days > 0,
});
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
submit = () => {
let daysNeedTobeSet = this.state.autoDelDays;
let reg = /^-?\d+$/;
let isvalid_days = reg.test(daysNeedTobeSet);
if (!isvalid_days || daysNeedTobeSet <= 0) {
this.setState({
errorInfo: gettext('Please enter a positive integer'),
});
return;
}
if (!this.state.isAutoDel) {
daysNeedTobeSet = 0; // if no auto del, give 0 to server
}
let repoID = this.props.repoID;
let message = gettext('Library old files auto delete days setted.');
seafileAPI.setRepoOldFilesAutoDelDays(repoID, daysNeedTobeSet).then(res => {
toaster.success(message);
this.props.toggleDialog();
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.submit();
e.preventDefault();
}
}
onChange = (e) => {
let days = e.target.value;
this.setState({
autoDelDays: days,
});
}
updateRadioCheck = (type) => {
if (type === 'noAutoDel') {
this.setState({
isAutoDel: false,
});
} else if (type === 'autoDel') {
this.setState({
isAutoDel: true,
});
}
}
render() {
return (
<Modal isOpen={true}>
<ModalHeader toggle={this.props.toggleDialog}>
{gettext('Auto deletion')}
</ModalHeader>
<ModalBody>
<Form>
<FormGroup check>
<Input type="radio" name="radio1" checked={!this.state.isAutoDel} onChange={() =>{this.updateRadioCheck('noAutoDel');}}/>{' '}
<Label>{gettext('Do not automatically delete files')}</Label>
</FormGroup>
<FormGroup check>
<Input type="radio" name="radio1" checked={this.state.isAutoDel} onChange={() =>{this.updateRadioCheck('autoDel');}}/>{' '}
<Label>{gettext('Automatically delete files that are not modified within certain days:')}</Label>
<Input
type="text"
className="expire-input"
value={this.state.autoDelDays}
onChange={this.onChange}
onKeyDown={this.handleKeyPress}
/>{' '}
<Label><span>{gettext('days')}</span></Label>
</FormGroup>
{this.state.errorInfo && <Alert color="danger">{this.state.errorInfo}</Alert>}
</Form>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.submit}>{gettext('Submit')}</Button>
</ModalFooter>
</Modal>
);
}
}
LibOldFilesAutoDelDialog.propTypes = propTypes;
export default LibOldFilesAutoDelDialog;

View File

@@ -20,6 +20,7 @@ import Rename from '../../components/rename';
import MylibRepoMenu from './mylib-repo-menu'; import MylibRepoMenu from './mylib-repo-menu';
import RepoAPITokenDialog from '../../components/dialog/repo-api-token-dialog'; import RepoAPITokenDialog from '../../components/dialog/repo-api-token-dialog';
import RepoShareUploadLinksDialog from '../../components/dialog/repo-share-upload-links-dialog'; import RepoShareUploadLinksDialog from '../../components/dialog/repo-share-upload-links-dialog';
import LibOldFilesAutoDelDialog from '../../components/dialog/lib-old-files-auto-del-dialog';
const propTypes = { const propTypes = {
repo: PropTypes.object.isRequired, repo: PropTypes.object.isRequired,
@@ -51,6 +52,7 @@ class MylibRepoListItem extends React.Component {
isAPITokenDialogShow: false, isAPITokenDialogShow: false,
isRepoShareUploadLinksDialogOpen: false, isRepoShareUploadLinksDialogOpen: false,
isRepoDeleted: false, isRepoDeleted: false,
isOldFilesAutoDelDialogOpen: false,
}; };
} }
@@ -111,6 +113,9 @@ class MylibRepoListItem extends React.Component {
case 'Share Links Admin': case 'Share Links Admin':
this.toggleRepoShareUploadLinksDialog(); this.toggleRepoShareUploadLinksDialog();
break; break;
case 'Old Files Auto Delete':
this.toggleOldFilesAutoDelDialog();
break;
default: default:
break; break;
} }
@@ -190,6 +195,10 @@ class MylibRepoListItem extends React.Component {
this.setState({isRepoShareUploadLinksDialogOpen: !this.state.isRepoShareUploadLinksDialogOpen}); this.setState({isRepoShareUploadLinksDialogOpen: !this.state.isRepoShareUploadLinksDialogOpen});
} }
toggleOldFilesAutoDelDialog = () => {
this.setState({isOldFilesAutoDelDialogOpen: !this.state.isOldFilesAutoDelDialogOpen});
}
onUnfreezedItem = () => { onUnfreezedItem = () => {
this.setState({ this.setState({
highlight: false, highlight: false,
@@ -452,6 +461,14 @@ class MylibRepoListItem extends React.Component {
/> />
</ModalPortal> </ModalPortal>
)} )}
{this.state.isOldFilesAutoDelDialogOpen && (
<ModalPortal>
<LibOldFilesAutoDelDialog
repoID={repo.repo_id}
toggleDialog={this.toggleOldFilesAutoDelDialog}
/>
</ModalPortal>
)}
</Fragment> </Fragment>
); );

View File

@@ -68,6 +68,7 @@ class MylibRepoMenu extends React.Component {
if (this.props.isPC && enableRepoSnapshotLabel) { if (this.props.isPC && enableRepoSnapshotLabel) {
operations.push('Label Current State'); operations.push('Label Current State');
} }
operations.push('Old Files Auto Delete');
return operations; return operations;
} }
@@ -113,6 +114,9 @@ class MylibRepoMenu extends React.Component {
case 'Share Links Admin': case 'Share Links Admin':
translateResult = gettext('Share Links Admin'); translateResult = gettext('Share Links Admin');
break; break;
case 'Old Files Auto Delete':
translateResult = gettext('Auto deletion');
break;
default: default:
break; break;
} }

View File

@@ -0,0 +1,88 @@
# Copyright (c) 2012-2019 Seafile Ltd.
# -*- coding: utf-8 -*-
import logging
from rest_framework import status
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from seaserv import seafile_api
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
from seahub.utils.repo import is_repo_admin
from seahub.constants import PERMISSION_READ_WRITE
from seahub.repo_auto_delete.models import RepoAutoDelete
logger = logging.getLogger(__name__)
class RepoAutoDeleteView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
def get(self, request, repo_id):
"""
Get auto del days of a repo
perm: rw, r, cloud-edit, preview
"""
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
perm = seafile_api.check_permission(repo_id, request.user.username)
if not perm:
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
try:
repo_auto_delete = RepoAutoDelete.objects.get(repo_id=repo_id)
except RepoAutoDelete.DoesNotExist:
return Response({'auto_delete_days': 0})
return Response({'auto_delete_days':repo_auto_delete.days})
def put(self, request, repo_id):
"""
Set auto del days of a repo
perm: repo admin
"""
auto_delete_days = request.data.get('auto_delete_days')
if not auto_delete_days:
error_msg = 'auto_delete_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
auto_delete_days = int(auto_delete_days)
except Exception as e:
error_msg = 'auto_del_days %s invalid.' % auto_delete_days
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if auto_delete_days < 0:
error_msg = 'auto_del_days %s invalid.' % auto_delete_days
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
username = request.user.username
if not is_repo_admin(username, repo_id):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try:
repo_auto_delete, _ = RepoAutoDelete.objects.update_or_create(repo_id=repo_id, defaults={'days':auto_delete_days})
except Exception as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error.')
return Response({'auto_delete_days':repo_auto_delete.days})

View File

@@ -0,0 +1,57 @@
# encoding: utf-8
import logging
from datetime import datetime
import stat
import os
import time
from django.core.management.base import BaseCommand
from seaserv import seafile_api, ccnet_api
from seahub.repo_auto_delete.models import RepoAutoDelete
logger = logging.getLogger(__name__)
def iterate_and_del_files_recursively(repo_id, path, days):
dirents = seafile_api.list_dir_by_path(repo_id, path)
for dirent in dirents:
if stat.S_ISDIR(dirent.mode):
iterate_and_del_files_recursively(repo_id, os.path.join(path, dirent.obj_name), days)
continue
mtime = dirent.mtime
cur_time = int(time.time())
time_delta = days * 24 * 60 * 60
if cur_time - time_delta > mtime:
file_full_path = os.path.join(path, dirent.obj_name)
seafile_api.del_file(repo_id, path, dirent.obj_name, 'seafevents')
logger.info('{} of {} deleted at {}.'.format(file_full_path, repo_id, cur_time))
class Command(BaseCommand):
help = 'scan repo_files_auto_del table, and delete old files if checked true'
label = "scan_repo_files_auto_del"
def handle(self, *args, **options):
logger.debug('Start scan repo_files_auto_del...')
self.stdout.write('[%s] Start scan repo_files_auto_del...\n' % datetime.now())
try:
self.do_action(*args, **options)
except Exception as e:
logger.error(e)
self.stdout.write('[%s] Finish scan repo_files_auto_del.\n' % datetime.now())
logger.debug('Finish scan repo_files_auto_del.')
def do_action(self, *args, **options):
repo_auto_deletes = RepoAutoDelete.objects.filter(days__gt=0)
for auto_del in repo_auto_deletes:
iterate_and_del_files_recursively(auto_del.repo_id, '/', auto_del.days)

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.25 on 2020-08-07 08:43
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='RepoAutoDelete',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('repo_id', models.CharField(db_index=True, max_length=36, unique=True)),
('days', models.IntegerField(default=0)),
],
options={
'db_table': 'repo_auto_delete',
},
),
]

View File

@@ -0,0 +1,24 @@
import logging
from django.db import models
from django.dispatch import receiver
from seahub.signals import repo_deleted
logger = logging.getLogger(__name__)
@receiver(repo_deleted)
def remove_repo_auto_del(sender, **kwargs):
repo_id = kwargs['repo_id']
try:
RepoAutoDelete.objects.filter(repo_id=repo_id).delete()
except Exception as e:
logger.error(e)
class RepoAutoDelete(models.Model):
repo_id = models.CharField(max_length=36, db_index=True, unique=True)
days = models.IntegerField(default=0)
class Meta:
db_table = 'repo_auto_delete'

View File

@@ -253,8 +253,10 @@ INSTALLED_APPS = [
'seahub.file_participants', 'seahub.file_participants',
'seahub.repo_api_tokens', 'seahub.repo_api_tokens',
'seahub.abuse_reports', 'seahub.abuse_reports',
'seahub.repo_auto_delete',
] ]
# Enable or disable view File Scan # Enable or disable view File Scan
# ENABLE_FILE_SCAN = True # ENABLE_FILE_SCAN = True

View File

@@ -175,6 +175,7 @@ from seahub.api2.endpoints.admin.virus_scan_records import AdminVirusFilesView,
AdminVirusFilesBatchView AdminVirusFilesBatchView
from seahub.api2.endpoints.file_participants import FileParticipantsView, FileParticipantView from seahub.api2.endpoints.file_participants import FileParticipantsView, FileParticipantView
from seahub.api2.endpoints.repo_related_users import RepoRelatedUsersView from seahub.api2.endpoints.repo_related_users import RepoRelatedUsersView
from seahub.api2.endpoints.repo_auto_delete import RepoAutoDeleteView
urlpatterns = [ urlpatterns = [
url(r'^accounts/', include('seahub.base.registration_urls')), url(r'^accounts/', include('seahub.base.registration_urls')),
@@ -364,6 +365,8 @@ urlpatterns = [
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/participant/$', FileParticipantView.as_view(), name='api-v2.1-file-participant'), url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/participant/$', FileParticipantView.as_view(), name='api-v2.1-file-participant'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/related-users/$', RepoRelatedUsersView.as_view(), name='api-v2.1-related-user'), url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/related-users/$', RepoRelatedUsersView.as_view(), name='api-v2.1-related-user'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/auto-delete/$', RepoAutoDeleteView.as_view(), name='api-v2.1-repo-auto-delete'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/share-links/$', RepoShareLinks.as_view(), name='api-v2.1-repo-share-links'), url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/share-links/$', RepoShareLinks.as_view(), name='api-v2.1-repo-share-links'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/share-links/(?P<token>[a-f0-9]+)/$', RepoShareLink.as_view(), name='api-v2.1-repo-share-link'), url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/share-links/(?P<token>[a-f0-9]+)/$', RepoShareLink.as_view(), name='api-v2.1-repo-share-link'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/upload-links/$', RepoUploadLinks.as_view(), name='api-v2.1-repo-upload-links'), url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/upload-links/$', RepoUploadLinks.as_view(), name='api-v2.1-repo-upload-links'),