mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-19 10:26:17 +00:00
backend finished
This commit is contained in:
669
seahub/api2/endpoints/metadata_manager.py
Normal file
669
seahub/api2/endpoints/metadata_manager.py
Normal file
@@ -0,0 +1,669 @@
|
|||||||
|
import logging, requests, stat, posixpath, jwt, time
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from seahub.api2.utils import api_error, to_python_boolean
|
||||||
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
|
from seahub.repo_metadata_enable.models import RepoMetadataEnable
|
||||||
|
from seahub.settings import ENABLE_METADATA_MANAGEMENT, MATEDATA_SERVER_URL, METEDATA_SERVER_SECRET_KEY
|
||||||
|
from seahub.views import check_folder_permission
|
||||||
|
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||||
|
from seaserv import seafile_api
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class __structure_table(object):
|
||||||
|
def __init__(self, id, name):
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
class __structure_column(object):
|
||||||
|
def __init__(self, key, name, type):
|
||||||
|
self.key = key
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
def to_build_column_dict(self):
|
||||||
|
return {
|
||||||
|
'name': self.name,
|
||||||
|
'type': self.type
|
||||||
|
}
|
||||||
|
|
||||||
|
TABLE = __structure_table('0001', 'Table1')
|
||||||
|
COLUMN_ID = __structure_column('0', '_id', 'text')
|
||||||
|
COLUMN_CREATOR = __structure_column('16', 'creator', 'text')
|
||||||
|
COLUMN_CREATE_TIME = __structure_column('17', 'create_time', 'date')
|
||||||
|
COLUMN_MODIFIER = __structure_column('18', 'modifier', 'text')
|
||||||
|
COLUMN_MODIFY_TIME = __structure_column('19', 'modify_time', 'date')
|
||||||
|
COLUMN_CURRENT_DIR = __structure_column('20', 'current_dir', 'text')
|
||||||
|
COLUMN_NAME = __structure_column('21', 'name', 'text')
|
||||||
|
COLUMN_IS_DIR = __structure_column('22', 'is_dir', 'text')
|
||||||
|
|
||||||
|
def gen_headers(repo_id):
|
||||||
|
payload = {
|
||||||
|
'exp': int(time.time()) + 300,
|
||||||
|
'base_id': repo_id
|
||||||
|
}
|
||||||
|
token = jwt.encode(payload, METEDATA_SERVER_SECRET_KEY, algorithm='HS256')
|
||||||
|
return {"Authorization": "Bearer %s" % token}
|
||||||
|
|
||||||
|
def scan_library(repo_id, current_dir = '/'):
|
||||||
|
'''
|
||||||
|
scan a library recursively
|
||||||
|
'''
|
||||||
|
dirents = seafile_api.list_dir_by_path(repo_id, current_dir) #this one only shows is_dir and name without modifier
|
||||||
|
scan_result = []
|
||||||
|
for tmp_dirent in dirents:
|
||||||
|
is_dir = stat.S_ISDIR(tmp_dirent.mode)
|
||||||
|
dirent = seafile_api.get_dirent_by_path(repo_id, posixpath.join(current_dir, tmp_dirent.obj_name))
|
||||||
|
scan_result.append([
|
||||||
|
'' if is_dir else dirent.modifier, #creator, the dir has not creator
|
||||||
|
timestamp_to_isoformat_timestr(dirent.mtime), #ctime
|
||||||
|
'' if is_dir else dirent.modifier, #modifier, the dir has not modifier
|
||||||
|
timestamp_to_isoformat_timestr(dirent.mtime), #mtime
|
||||||
|
current_dir,
|
||||||
|
dirent.obj_name, #name
|
||||||
|
'True' if is_dir else 'False'
|
||||||
|
])
|
||||||
|
if is_dir:
|
||||||
|
scan_result += scan_library(repo_id, posixpath.join(current_dir, dirent.obj_name))
|
||||||
|
return scan_result
|
||||||
|
|
||||||
|
def check_and_rollback(response, repo_id, headers):
|
||||||
|
if response.status_code >= 200 and response.status_code < 300:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}'
|
||||||
|
requests.delete(url, headers=headers)
|
||||||
|
if isinstance(response.reason, bytes):
|
||||||
|
try:
|
||||||
|
reason = response.reason.decode("utf-8")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
reason = response.reason.decode("iso-8859-1")
|
||||||
|
else:
|
||||||
|
reason = response.reason
|
||||||
|
return response.status_code, reason
|
||||||
|
|
||||||
|
def initial_metadata_base(repo_id):
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
|
||||||
|
#create a metadata base for repo_id
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}'
|
||||||
|
response = requests.post(url, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
# Add columns: creator, create_time, modifier, modify_time, current_dir, name
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/columns'
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_CREATOR.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_CREATE_TIME.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_MODIFIER.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_MODIFY_TIME.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_CURRENT_DIR.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_NAME.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column': COLUMN_IS_DIR.to_build_column_dict()
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
response.raise_for_status
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
#scan files and dirs
|
||||||
|
rows = scan_library(repo_id)
|
||||||
|
|
||||||
|
#insert current metadata to md server from root dir
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/rows'
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column_keys': [
|
||||||
|
COLUMN_CREATOR.key,
|
||||||
|
COLUMN_CREATE_TIME.key,
|
||||||
|
COLUMN_MODIFIER.key,
|
||||||
|
COLUMN_MODIFY_TIME.key,
|
||||||
|
COLUMN_CURRENT_DIR.key,
|
||||||
|
COLUMN_NAME.key,
|
||||||
|
COLUMN_IS_DIR.key
|
||||||
|
],
|
||||||
|
'rows': rows
|
||||||
|
}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if err := check_and_rollback(response, repo_id, headers):
|
||||||
|
return err
|
||||||
|
|
||||||
|
class MetadataManager(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated, )
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def get(self, request, repo_id):
|
||||||
|
'''
|
||||||
|
check the repo has enabled the metadata manager or not
|
||||||
|
'''
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return Response({
|
||||||
|
'enable': True if RepoMetadataEnable.objects.filter(repo_id=repo_id) else False
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
def post(self, request, repo_id):
|
||||||
|
'''
|
||||||
|
enable a new repo's metadata manager
|
||||||
|
'''
|
||||||
|
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# check dose the repo have opened metadata manager
|
||||||
|
if RepoMetadataEnable.objects.filter(repo_id=repo_id):
|
||||||
|
error_msg = f'The metadata module is enable for repo {repo_id}.'
|
||||||
|
return api_error(status.HTTP_409_CONFLICT, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
err = initial_metadata_base(repo_id)
|
||||||
|
if err:
|
||||||
|
status_code, reason = err
|
||||||
|
logger.error(f'Metadata initial err, {status_code}: {reason}')
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {status_code}: {reason}')
|
||||||
|
|
||||||
|
try:
|
||||||
|
repo_metadata_enable = RepoMetadataEnable(repo_id=repo_id)
|
||||||
|
repo_metadata_enable.save()
|
||||||
|
return Response({
|
||||||
|
'success': True
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
#rollback
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}'
|
||||||
|
requests.delete(url, headers=headers)
|
||||||
|
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
def delete(self, request, repo_id):
|
||||||
|
'''
|
||||||
|
remove a repo's metadata manager
|
||||||
|
'''
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
# check dose the repo have opened metadata manager
|
||||||
|
enable_recode = RepoMetadataEnable.objects.filter(repo_id=repo_id)
|
||||||
|
if not enable_recode:
|
||||||
|
error_msg = f'The repo {repo_id} has not enable the metadata manager.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}'
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
response = requests.delete(url, headers=headers)
|
||||||
|
if response.status_code < 200 or response.status_code > 299:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
||||||
|
|
||||||
|
enable_recode.delete()
|
||||||
|
return Response({
|
||||||
|
'success': True
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
class MetadataManagerRecords(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated, )
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def get(self, request, repo_id):
|
||||||
|
'''
|
||||||
|
fetch a metadata results
|
||||||
|
request body:
|
||||||
|
parent_dir: optional, if not specify, search from all dirs
|
||||||
|
name: optional, if not specify, search from all objects
|
||||||
|
page: optional, the current page
|
||||||
|
perpage: optional, if use page, default is 25
|
||||||
|
is_dir: optional, True or False
|
||||||
|
'''
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
|
||||||
|
#args check
|
||||||
|
parent_dir = request.data.get('parent_dir')
|
||||||
|
name = request.data.get('name')
|
||||||
|
page = request.data.get('page')
|
||||||
|
perpage = request.data.get('perpage')
|
||||||
|
is_dir = request.data.get('is_dir')
|
||||||
|
|
||||||
|
if page:
|
||||||
|
try:
|
||||||
|
page = int(page)
|
||||||
|
except ValueError:
|
||||||
|
error_msg = 'Page is not vaild.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
if perpage:
|
||||||
|
try:
|
||||||
|
perpage = int(perpage)
|
||||||
|
except ValueError:
|
||||||
|
error_msg = 'Perpage is not vaild.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
else:
|
||||||
|
perpage = 25
|
||||||
|
|
||||||
|
if is_dir:
|
||||||
|
try:
|
||||||
|
is_dir = to_python_boolean(is_dir)
|
||||||
|
except:
|
||||||
|
error_msg = 'is_dir is not vaild.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# metadata enable check
|
||||||
|
if not RepoMetadataEnable.objects.filter(repo_id=repo_id):
|
||||||
|
error_msg = f'The metadata module is not enable for repo {repo_id}.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
sql = f'SELECT `{COLUMN_ID.name}`, `{COLUMN_CREATOR.name}`, `{COLUMN_CREATE_TIME.name}`, `{COLUMN_MODIFIER.name}`, `{COLUMN_MODIFY_TIME.name}`, `{COLUMN_CURRENT_DIR.name}`, `{COLUMN_NAME.name}`, `{COLUMN_IS_DIR.name}` FROM `{TABLE.name}`'
|
||||||
|
|
||||||
|
if parent_dir:
|
||||||
|
sql += f' WHERE `{COLUMN_CURRENT_DIR.name}` = "{parent_dir}"'
|
||||||
|
if name:
|
||||||
|
sql += f' AND `{COLUMN_NAME.name}` = "{name}"'
|
||||||
|
|
||||||
|
if is_dir:
|
||||||
|
sql += f' AND `{COLUMN_IS_DIR.name}` = "{is_dir}"'
|
||||||
|
elif name:
|
||||||
|
sql += f' WHERE `{COLUMN_NAME.name}` = "{name}"'
|
||||||
|
if is_dir:
|
||||||
|
sql += f' AND `{COLUMN_IS_DIR.name}` = "{is_dir}"'
|
||||||
|
elif is_dir:
|
||||||
|
sql += f' WHERE `{COLUMN_IS_DIR.name}` = "{is_dir}"'
|
||||||
|
|
||||||
|
if page:
|
||||||
|
sql += f' LIMIT {(page - 1) * perpage}, {page * perpage}'
|
||||||
|
|
||||||
|
sql += ';'
|
||||||
|
|
||||||
|
#query_result
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/query'
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
response = requests.post(url, json={'sql': sql}, headers=headers)
|
||||||
|
if response.status_code < 200 or response.status_code > 299:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
||||||
|
|
||||||
|
response_results = response.json()['results']
|
||||||
|
if response_results:
|
||||||
|
results = [
|
||||||
|
{
|
||||||
|
COLUMN_ID.name: result[0],
|
||||||
|
COLUMN_CREATOR.name: result[1],
|
||||||
|
COLUMN_CREATE_TIME.name: result[2],
|
||||||
|
COLUMN_MODIFIER.name: result[3],
|
||||||
|
COLUMN_MODIFY_TIME.name: result[4],
|
||||||
|
COLUMN_CURRENT_DIR.name: result[5],
|
||||||
|
COLUMN_NAME.name: result[6],
|
||||||
|
COLUMN_IS_DIR.name: True if result[7] == 'True' else False
|
||||||
|
}
|
||||||
|
for result in response_results
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
results = []
|
||||||
|
return Response({
|
||||||
|
'results': results
|
||||||
|
})
|
||||||
|
|
||||||
|
def post(self, request, repo_id):
|
||||||
|
'''
|
||||||
|
add a metadata results
|
||||||
|
request body:
|
||||||
|
parent_dir: required, if not specify, search from all dirs
|
||||||
|
name: required, if not specify, search from all objects
|
||||||
|
'''
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# args check
|
||||||
|
parent_dir = request.data.get('parent_dir')
|
||||||
|
name = request.data.get('name')
|
||||||
|
|
||||||
|
if not parent_dir:
|
||||||
|
error_msg = 'parent_dir is not specified.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
error_msg = 'name is not specified.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# metadata enable check
|
||||||
|
if not RepoMetadataEnable.objects.filter(repo_id=repo_id):
|
||||||
|
error_msg = f'The metadata module is not enable for repo {repo_id}.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
# dirent check
|
||||||
|
dirent_path = posixpath.join(parent_dir, name)
|
||||||
|
dirent = seafile_api.get_dirent_by_path(repo_id, dirent_path)
|
||||||
|
if not dirent:
|
||||||
|
error_msg = 'dirent %s not found.' % dirent_path
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# check the current dirent exists or not
|
||||||
|
is_dir = stat.S_ISDIR(dirent.mode)
|
||||||
|
sql = f'SELECT `{COLUMN_ID.name}` FROM `{TABLE.name}` WHERE `{COLUMN_CURRENT_DIR.name}` = "{parent_dir}" AND `{COLUMN_NAME.name}` = "{name}" AND `{COLUMN_IS_DIR.name}` = "{True if is_dir else False}";'
|
||||||
|
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/query'
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
response = requests.post(url, json={'sql': sql}, headers=headers)
|
||||||
|
if response.status_code < 200 or response.status_code > 299:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
||||||
|
|
||||||
|
query_result = response.json().get('results')
|
||||||
|
if query_result:
|
||||||
|
error_msg = 'dirent %s has inserted in metadata base.' % dirent_path
|
||||||
|
return api_error(status.HTTP_409_CONFLICT, error_msg)
|
||||||
|
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/rows'
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column_keys': [
|
||||||
|
COLUMN_CREATOR.key,
|
||||||
|
COLUMN_CREATE_TIME.key,
|
||||||
|
COLUMN_MODIFIER.key,
|
||||||
|
COLUMN_MODIFY_TIME.key,
|
||||||
|
COLUMN_CURRENT_DIR.key,
|
||||||
|
COLUMN_NAME.key,
|
||||||
|
COLUMN_IS_DIR.key
|
||||||
|
],
|
||||||
|
'rows': [[
|
||||||
|
'' if is_dir else dirent.modifier, #creator, the dir has not creator
|
||||||
|
timestamp_to_isoformat_timestr(dirent.mtime), #ctime
|
||||||
|
'' if is_dir else dirent.modifier, #modifier, the dir has not modifier
|
||||||
|
timestamp_to_isoformat_timestr(dirent.mtime), #mtime
|
||||||
|
parent_dir,
|
||||||
|
dirent.obj_name, #name
|
||||||
|
'True' if is_dir else 'False'
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if response.status_code >= 200 and response.status_code <= 299:
|
||||||
|
return Response({
|
||||||
|
'success': True
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
||||||
|
|
||||||
|
class MetadataManagerRecord(APIView):
|
||||||
|
#authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
#permission_classes = (IsAuthenticated, )
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
def put(self, request, repo_id, record_id):
|
||||||
|
'''
|
||||||
|
modify a metadata base recode by the record_id
|
||||||
|
for simplfying the precudures, all parameters are required
|
||||||
|
request body:
|
||||||
|
creator, required
|
||||||
|
create_time, required
|
||||||
|
modifier, required,
|
||||||
|
modify_time, required
|
||||||
|
current_dir, required
|
||||||
|
name, required
|
||||||
|
'''
|
||||||
|
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
creator = request.data.get('creator')
|
||||||
|
create_time = request.data.get('create_time')
|
||||||
|
modifier = request.data.get('modifier')
|
||||||
|
modify_time = request.data.get('modify_time')
|
||||||
|
current_dir = request.data.get('current_dir')
|
||||||
|
name = request.data.get('name')
|
||||||
|
|
||||||
|
#args check
|
||||||
|
for body_key in ('creator', 'create_time', 'modifier', 'modify_time', 'current_dir', 'name'):
|
||||||
|
if eval(body_key) is None:
|
||||||
|
error_msg = f"{body_key} is not specified"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# metadata enable check
|
||||||
|
if not RepoMetadataEnable.objects.filter(repo_id=repo_id):
|
||||||
|
error_msg = f'The metadata module is not enable for repo {repo_id}.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
# dirent check
|
||||||
|
dirent_path = posixpath.join(current_dir, name)
|
||||||
|
dirent = seafile_api.get_dirent_by_path(repo_id, dirent_path)
|
||||||
|
if not dirent:
|
||||||
|
error_msg = 'dirent %s not found.' % dirent_path
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# check the current dirent exists or not
|
||||||
|
is_dir = stat.S_ISDIR(dirent.mode)
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/query'
|
||||||
|
|
||||||
|
sql = f'SELECT `{COLUMN_ID.name}` FROM `{TABLE.name}` WHERE `{COLUMN_CURRENT_DIR.name}` = "{current_dir}" AND `{COLUMN_NAME.name}` = "{name}" AND `{COLUMN_ID.name}` != "{record_id}" AND `{COLUMN_IS_DIR.name}` = "{True if is_dir else False}";'
|
||||||
|
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
response = requests.post(url, json={'sql': sql}, headers=headers)
|
||||||
|
if response.status_code < 200 or response.status_code > 299:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code} in querying result: {response.reason}')
|
||||||
|
|
||||||
|
query_result = response.json().get('results')
|
||||||
|
if query_result:
|
||||||
|
error_msg = f'The {"folder" if is_dir else "file"} {dirent_path} is exists in the metadata base'
|
||||||
|
return api_error(status.HTTP_409_CONFLICT, error_msg)
|
||||||
|
|
||||||
|
# dirent type originality check
|
||||||
|
sql = f'SELECT `{COLUMN_ID.name}` FROM `{TABLE.name}` WHERE `{COLUMN_ID.name}` = "{record_id}" AND `{COLUMN_IS_DIR.name}` != "{True if is_dir else False}";'
|
||||||
|
|
||||||
|
response = requests.post(url, json={'sql': sql}, headers=headers)
|
||||||
|
if response.status_code < 200 or response.status_code > 299:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
||||||
|
|
||||||
|
query_result = response.json().get('results')
|
||||||
|
if query_result:
|
||||||
|
error_msg = f'The type of new dirent {dirent_path} is not matched with the original type'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/rows'
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'column_keys': [
|
||||||
|
COLUMN_ID.key,
|
||||||
|
COLUMN_CREATOR.key,
|
||||||
|
COLUMN_CREATE_TIME.key,
|
||||||
|
COLUMN_MODIFIER.key,
|
||||||
|
COLUMN_MODIFY_TIME.key,
|
||||||
|
COLUMN_CURRENT_DIR.key,
|
||||||
|
COLUMN_NAME.key
|
||||||
|
],
|
||||||
|
'rows': [[
|
||||||
|
record_id,
|
||||||
|
creator,
|
||||||
|
create_time,
|
||||||
|
modifier,
|
||||||
|
modify_time,
|
||||||
|
current_dir,
|
||||||
|
name
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
response = requests.put(url, json=data, headers=headers)
|
||||||
|
if response.status_code >= 200 and response.status_code < 299:
|
||||||
|
return Response({
|
||||||
|
'success': True
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
||||||
|
|
||||||
|
def delete(self, request, repo_id, record_id):
|
||||||
|
if not ENABLE_METADATA_MANAGEMENT or not MATEDATA_SERVER_URL:
|
||||||
|
error_msg = "Function is not supported"
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
# metadata enable check
|
||||||
|
if not RepoMetadataEnable.objects.filter(repo_id=repo_id):
|
||||||
|
error_msg = f'The metadata module is not enable for repo {repo_id}.'
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
# recource check
|
||||||
|
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)
|
||||||
|
|
||||||
|
# permission check
|
||||||
|
'''permission = check_folder_permission(request, repo_id, '/')
|
||||||
|
if not permission:
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
'''
|
||||||
|
|
||||||
|
url = f'{MATEDATA_SERVER_URL}/api/v1/base/{repo_id}/rows'
|
||||||
|
data = {
|
||||||
|
'table_id': TABLE.id,
|
||||||
|
'row_ids': [
|
||||||
|
record_id
|
||||||
|
]
|
||||||
|
}
|
||||||
|
headers = gen_headers(repo_id)
|
||||||
|
response = requests.delete(url, json=data, headers=headers)
|
||||||
|
if response.status_code >= 200 and response.status_code <= 299:
|
||||||
|
return Response({
|
||||||
|
'success': True
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return api_error(status.HTTP_503_SERVICE_UNAVAILABLE, f'error from metadata server with code {response.status_code}: {response.reason}')
|
0
seahub/repo_metadata_enable/__init__.py
Normal file
0
seahub/repo_metadata_enable/__init__.py
Normal file
11
seahub/repo_metadata_enable/models.py
Normal file
11
seahub/repo_metadata_enable/models.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import logging
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class RepoMetadataEnable(models.Model):
|
||||||
|
|
||||||
|
repo_id = models.CharField(max_length=36, db_index=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'repo_metadata_enable'
|
@@ -269,6 +269,7 @@ INSTALLED_APPS = [
|
|||||||
'seahub.dingtalk',
|
'seahub.dingtalk',
|
||||||
'seahub.file_participants',
|
'seahub.file_participants',
|
||||||
'seahub.repo_api_tokens',
|
'seahub.repo_api_tokens',
|
||||||
|
'seahub.repo_metadata_enable',
|
||||||
'seahub.abuse_reports',
|
'seahub.abuse_reports',
|
||||||
'seahub.repo_auto_delete',
|
'seahub.repo_auto_delete',
|
||||||
'seahub.ocm',
|
'seahub.ocm',
|
||||||
@@ -887,6 +888,13 @@ SEATABLE_EX_PROPS_BASE_API_TOKEN = ''
|
|||||||
EX_PROPS_TABLE = ''
|
EX_PROPS_TABLE = ''
|
||||||
EX_EDITABLE_COLUMNS = []
|
EX_EDITABLE_COLUMNS = []
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# metadata server properties #
|
||||||
|
##############################
|
||||||
|
ENABLE_METADATA_MANAGEMENT = False
|
||||||
|
MATEDATA_SERVER_URL = None
|
||||||
|
METEDATA_SERVER_SECRET_KEY = ''
|
||||||
|
|
||||||
d = os.path.dirname
|
d = os.path.dirname
|
||||||
EVENTS_CONFIG_FILE = os.environ.get(
|
EVENTS_CONFIG_FILE = os.environ.get(
|
||||||
'EVENTS_CONFIG_FILE',
|
'EVENTS_CONFIG_FILE',
|
||||||
|
@@ -206,6 +206,8 @@ from seahub.ai.apis import LibrarySdocIndexes, Search, LibrarySdocIndex, TaskSta
|
|||||||
from seahub.wiki2.views import wiki_view
|
from seahub.wiki2.views import wiki_view
|
||||||
from seahub.api2.endpoints.wiki2 import Wikis2View, Wiki2View, Wiki2ConfigView, Wiki2PagesView, Wiki2PageView
|
from seahub.api2.endpoints.wiki2 import Wikis2View, Wiki2View, Wiki2ConfigView, Wiki2PagesView, Wiki2PageView
|
||||||
from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView
|
from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView
|
||||||
|
from seahub.api2.endpoints.metadata_manager import MetadataManagerRecords, MetadataManager, MetadataManagerRecord
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('accounts/', include('seahub.base.registration_urls')),
|
path('accounts/', include('seahub.base.registration_urls')),
|
||||||
@@ -434,6 +436,9 @@ urlpatterns = [
|
|||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/participants/$', FileParticipantsView.as_view(), name='api-v2.1-file-participants'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/participants/$', FileParticipantsView.as_view(), name='api-v2.1-file-participants'),
|
||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/participant/$', FileParticipantView.as_view(), name='api-v2.1-file-participant'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/participant/$', FileParticipantView.as_view(), name='api-v2.1-file-participant'),
|
||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/related-users/$', RepoRelatedUsersView.as_view(), name='api-v2.1-related-user'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/related-users/$', RepoRelatedUsersView.as_view(), name='api-v2.1-related-user'),
|
||||||
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/metadata/$', MetadataManager.as_view(), name='api-v2.1-metadata'),
|
||||||
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/metadata/records/$', MetadataManagerRecords.as_view(), name='api-v2.1-metadata-records'),
|
||||||
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/metadata/records/(?P<record_id>[A-Za-z0-9_]+)/$', MetadataManagerRecord.as_view(), name='api-v2.1-metadata-record'),
|
||||||
|
|
||||||
## user:file:extended-props
|
## user:file:extended-props
|
||||||
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/extended-properties/$', ExtendedPropertiesView.as_view(), name='api-v2.1-extended-properties'),
|
re_path(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/extended-properties/$', ExtendedPropertiesView.as_view(), name='api-v2.1-extended-properties'),
|
||||||
|
Reference in New Issue
Block a user