# HG changeset patch # User Marcin Kuzminski # Date 2019-01-28 16:17:37 # Node ID 206694bc6e87fccaabf2d8a898cfaf50b81fbb42 # Parent 6623e525500223ca8f217d184bdaf3dac000a997 API: added basic upload api for the file storage diff --git a/rhodecode/api/views/server_api.py b/rhodecode/api/views/server_api.py --- a/rhodecode/api/views/server_api.py +++ b/rhodecode/api/views/server_api.py @@ -21,6 +21,9 @@ import inspect import logging import itertools +import base64 + +from pyramid import compat from rhodecode.api import ( jsonrpc_method, JSONRPCError, JSONRPCForbidden, find_methods) @@ -36,6 +39,9 @@ from rhodecode.lib.utils2 import safe_in from rhodecode.model.db import UserIpMap from rhodecode.model.scm import ScmModel from rhodecode.model.settings import VcsSettingsModel +from rhodecode.apps.upload_store import utils +from rhodecode.apps.upload_store.exceptions import FileNotAllowedException, \ + FileOverSizeException log = logging.getLogger(__name__) @@ -412,3 +418,64 @@ def store_exception(request, apiuser, ex 'admin_settings_exception_tracker_show', exception_id=exc_id) return {'exc_id': exc_id, 'exc_url': exc_url} + +@jsonrpc_method() +def upload_file(request, apiuser, filename, content): + """ + Upload API for the file_store + + Example usage from CLI:: + rhodecode-api --instance-name=enterprise-1 upload_file "{\"content\": \"$(cat image.jpg | base64)\", \"filename\":\"image.jpg\"}" + + + This command can only be run using an |authtoken| with admin rights to + the specified repository. + + This command takes the following options: + + :param apiuser: This is filled automatically from the |authtoken|. + :type apiuser: AuthUser + :param filename: name of the file uploaded + :type filename: str + :param content: base64 encoded content of the uploaded file + :type prefix: str + + Example output: + + .. code-block:: bash + + id : + result: { + "access_path": "/_file_store/download/84d156f7-8323-4ad3-9fce-4a8e88e1deaf-0.jpg", + "access_path_fqn": "http://server.domain.com/_file_store/download/84d156f7-8323-4ad3-9fce-4a8e88e1deaf-0.jpg", + "store_fid": "84d156f7-8323-4ad3-9fce-4a8e88e1deaf-0.jpg" + } + error : null + """ + if not has_superadmin_permission(apiuser): + raise JSONRPCForbidden() + + storage = utils.get_file_storage(request.registry.settings) + + try: + file_obj = compat.NativeIO(base64.decodestring(content)) + except Exception as exc: + raise JSONRPCError('File `{}` content decoding error: {}.'.format(filename, exc)) + + metadata = { + 'filename': filename, + 'size': '', # filled by save_file + 'user_uploaded': {'username': apiuser.username, + 'user_id': apiuser.user_id, + 'ip': apiuser.ip_addr}} + try: + store_fid = storage.save_file(file_obj, filename, metadata=metadata) + except FileNotAllowedException: + raise JSONRPCError('File `{}` is not allowed.'.format(filename)) + + except FileOverSizeException: + raise JSONRPCError('File `{}` is exceeding allowed limit.'.format(filename)) + + return {'store_fid': store_fid, + 'access_path_fqn': request.route_url('download_file', fid=store_fid), + 'access_path': request.route_path('download_file', fid=store_fid)} diff --git a/rhodecode/apps/upload_store/local_store.py b/rhodecode/apps/upload_store/local_store.py --- a/rhodecode/apps/upload_store/local_store.py +++ b/rhodecode/apps/upload_store/local_store.py @@ -19,6 +19,7 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import os +import time import shutil from rhodecode.lib.ext_json import json @@ -26,6 +27,8 @@ from rhodecode.apps.upload_store import from rhodecode.apps.upload_store.extensions import resolve_extensions from rhodecode.apps.upload_store.exceptions import FileNotAllowedException +METADATA_VER = 'v1' + class LocalFileStorage(object): @@ -157,7 +160,8 @@ class LocalFileStorage(object): if metadata: size = os.stat(path).st_size - metadata.update({'size': size}) + metadata.update({'size': size, "time": time.time(), + "meta_ver": METADATA_VER}) with open(os.path.join(dest_directory, filename_meta), "wb") as dest_meta: dest_meta.write(json.dumps(metadata))