server/uploads: add file upload api

This commit is contained in:
rr-
2017-01-07 11:59:43 +01:00
parent f00cc5f3fa
commit 036fa9ee39
9 changed files with 136 additions and 19 deletions

View File

@ -6,3 +6,4 @@ import szurubooru.api.tag_category_api
import szurubooru.api.comment_api
import szurubooru.api.password_reset_api
import szurubooru.api.snapshot_api
import szurubooru.api.upload_api

View File

@ -0,0 +1,10 @@
from szurubooru.rest import routes
from szurubooru.func import auth, file_uploads
@routes.post('/uploads/?')
def create_temporary_file(ctx, _params=None):
auth.verify_privilege(ctx.user, 'uploads:create')
content = ctx.get_file('content', required=True, allow_tokens=False)
token = file_uploads.save(content)
return {'token': token}

View File

@ -36,6 +36,10 @@ class MissingRequiredFileError(ValidationError):
pass
class MissingOrExpiredRequiredFileError(MissingRequiredFileError):
pass
class MissingRequiredParameterError(ValidationError):
pass

View File

@ -1,11 +1,13 @@
''' Exports create_app. '''
import os
import time
import logging
import threading
import coloredlogs
import sqlalchemy.orm.exc
from szurubooru import config, errors, rest
from szurubooru.func import posts
from szurubooru.func import posts, file_uploads
# pylint: disable=unused-import
from szurubooru import api, middleware
@ -79,6 +81,15 @@ def validate_config():
raise errors.ConfigError('Database is not configured')
def purge_old_uploads():
while True:
try:
file_uploads.purge_old_uploads()
except Exception as ex:
logging.exception(ex)
time.sleep(60 * 5)
def create_app():
''' Create a WSGI compatible App object. '''
validate_config()
@ -88,6 +99,9 @@ def create_app():
if config.config['show_sql']:
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
purge_thread = threading.Thread(target=purge_old_uploads)
purge_thread.daemon = True
purge_thread.start()
posts.populate_reverse_search()
rest.errors.handle(errors.AuthError, _on_auth_error)

View File

@ -0,0 +1,29 @@
import datetime
from szurubooru.func import files, util
MAX_MINUTES = 60
def _get_path(checksum):
return 'temporary-uploads/%s.dat' % checksum
def purge_old_uploads():
now = datetime.datetime.now()
for file in files.scan('temporary-uploads'):
file_time = datetime.datetime.fromtimestamp(file.stat().st_ctime)
if now - file_time > datetime.timedelta(minutes=MAX_MINUTES):
files.delete('temporary-uploads/%s' % file.name)
def get(checksum):
return files.get('temporary-uploads/%s.dat' % checksum)
def save(content):
checksum = util.get_sha1(content)
path = _get_path(checksum)
if not files.has(path):
files.save(path, content)
return checksum

View File

@ -16,6 +16,12 @@ def has(path):
return os.path.exists(_get_full_path(path))
def scan(path):
if has(path):
return os.scandir(_get_full_path(path))
return []
def move(source_path, target_path):
return os.rename(_get_full_path(source_path), _get_full_path(target_path))

View File

@ -1,5 +1,5 @@
from szurubooru import errors
from szurubooru.func import net
from szurubooru.func import net, file_uploads
def _lower_first(source):
@ -43,18 +43,26 @@ class Context:
def get_header(self, name):
return self._headers.get(name, None)
def has_file(self, name):
return name in self._files or name + 'Url' in self._params
def has_file(self, name, allow_tokens=True):
return (name in self._files
or name + 'Url' in self._params
or (allow_tokens and name + 'Token' in self._params))
def get_file(self, name, required=False):
def get_file(self, name, required=False, allow_tokens=True):
ret = None
if name in self._files:
return self._files[name]
if name + 'Url' in self._params:
return net.download(self._params[name + 'Url'])
if not required:
return None
raise errors.MissingRequiredFileError(
'Required file %r is missing.' % name)
ret = self._files[name]
elif name + 'Url' in self._params:
ret = net.download(self._params[name + 'Url'])
elif allow_tokens and name + 'Token' in self._params:
ret = file_uploads.get(self._params[name + 'Token'])
if required and not ret:
raise errors.MissingOrExpiredRequiredFileError(
'Required file %r is missing or has expired.' % name)
if required and not ret:
raise errors.MissingRequiredFileError(
'Required file %r is missing.' % name)
return ret
def has_param(self, name):
return name in self._params