159 lines
5.7 KiB
Python
159 lines
5.7 KiB
Python
# passerelle - uniform access to multiple data sources and services
|
|
# Copyright (C) 2023 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import base64
|
|
import binascii
|
|
import json
|
|
import urllib
|
|
|
|
import requests
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from passerelle.base.models import BaseResource, HTTPResource
|
|
from passerelle.utils.api import endpoint
|
|
from passerelle.utils.jsonresponse import APIError
|
|
|
|
from . import schemas
|
|
|
|
|
|
class Filr(BaseResource, HTTPResource):
|
|
base_url = models.URLField(
|
|
verbose_name=_('Webservice Base URL'),
|
|
)
|
|
|
|
category = _('File Storage')
|
|
|
|
class Meta:
|
|
verbose_name = _('Filr')
|
|
|
|
def _call(self, path, method='get', json_data=None, data=None, headers=None, params=None):
|
|
kwargs = {}
|
|
if data:
|
|
kwargs['data'] = data
|
|
if json_data:
|
|
kwargs['json'] = json_data
|
|
if headers:
|
|
kwargs['headers'] = headers
|
|
if params:
|
|
kwargs['params'] = params
|
|
try:
|
|
resp = self.requests.request(
|
|
method=method, url=urllib.parse.urljoin(self.base_url, path), **kwargs
|
|
)
|
|
except (requests.Timeout, requests.RequestException) as e:
|
|
raise APIError(str(e))
|
|
try:
|
|
resp.raise_for_status()
|
|
except requests.RequestException as main_exc:
|
|
try:
|
|
err_data = resp.json()
|
|
except (json.JSONDecodeError, requests.exceptions.RequestException):
|
|
err_data = {'response_text': resp.text}
|
|
raise APIError(str(main_exc), data=err_data)
|
|
|
|
content_type = resp.headers.get('Content-Type')
|
|
if content_type and content_type.startswith('application/json') and resp.status_code != 204:
|
|
try:
|
|
return resp.json()
|
|
except (json.JSONDecodeError, requests.exceptions.JSONDecodeError) as e:
|
|
raise APIError(str(e))
|
|
|
|
return resp.text
|
|
|
|
@endpoint(
|
|
description=_('Upload a file'),
|
|
post={
|
|
'request_body': {'schema': {'application/json': schemas.UPLOAD}},
|
|
},
|
|
)
|
|
def upload(self, request, post_data):
|
|
root_folder_id, folder_name = post_data['root_folder_id'], post_data['folder_name']
|
|
try:
|
|
file_content = base64.b64decode(post_data['file']['content'])
|
|
except (TypeError, binascii.Error):
|
|
raise APIError('invalid base64 string', http_status=400)
|
|
filename = post_data.get('filename') or post_data['file']['filename']
|
|
|
|
# get or create folder
|
|
folder_id = None
|
|
root_folder_info = self._call(
|
|
'rest/folders/%s/library_folders' % root_folder_id, params={'title': folder_name}
|
|
)
|
|
for folder in root_folder_info.get('items', []):
|
|
if folder.get('title') == folder_name:
|
|
folder_id = str(folder.get('id'))
|
|
break
|
|
else:
|
|
folder_info = self._call(
|
|
'rest/folders/%s/library_folders' % root_folder_id,
|
|
method='post',
|
|
json_data={'title': folder_name},
|
|
)
|
|
folder_id = str(folder_info['id'])
|
|
|
|
# upload file
|
|
file_info = self._call(
|
|
'rest/folders/%s/library_files' % folder_id,
|
|
method='post',
|
|
params={'file_name': filename},
|
|
headers={'Content-Type': 'application/octet-stream'},
|
|
data=file_content,
|
|
)
|
|
return {'data': {'folder_id': folder_id, 'file_info': file_info}}
|
|
|
|
@endpoint(
|
|
name='share-folder',
|
|
description=_('Share a folder to external users'),
|
|
post={
|
|
'request_body': {'schema': {'application/json': schemas.SHARE_FOLDER}},
|
|
'input_example': {
|
|
'folder_id': '1234',
|
|
'emails/0': 'foo@invalid',
|
|
'emails/1': 'bar@invalid',
|
|
'days_to_expire': '30',
|
|
},
|
|
},
|
|
)
|
|
def share_folder(self, request, post_data):
|
|
data = []
|
|
for email in post_data['emails']:
|
|
if email:
|
|
share_info = self._call(
|
|
'rest/folders/%s/shares' % post_data['folder_id'],
|
|
method='post',
|
|
params={'notify': 'true'},
|
|
json_data={
|
|
'days_to_expire': post_data['days_to_expire'],
|
|
'recipient': {'type': 'external_user', 'email': email},
|
|
'access': {'role': 'VIEWER'},
|
|
},
|
|
)
|
|
data.append(share_info)
|
|
if not data:
|
|
raise APIError('no valid email in emails', http_status=400)
|
|
return {'data': data}
|
|
|
|
@endpoint(
|
|
name='delete-folder',
|
|
methods=['post'],
|
|
description=_('Delete a folder'),
|
|
post={'request_body': {'schema': {'application/json': schemas.DELETE_FOLDER}}},
|
|
)
|
|
def delete_folder(self, request, post_data):
|
|
delete_infos = self._call('rest/folders/%s' % post_data['folder_id'], method='delete')
|
|
return {'data': delete_infos}
|