import json from time import time from django.shortcuts import render from django.http import HttpResponse, Http404, JsonResponse from django.views.decorators.csrf import csrf_exempt from django.urls import reverse from django.urls.exceptions import NoReverseMatch from django.conf import settings from . import es from .models import Collection from . import signals from .ratelimit import limit_user, get_request_limits def JsonErrorResponse(reason, status=400): return JsonResponse({'status': 'error', 'reason': reason}, status=status) def collections_acl(user, collections_arg): available = list(Collection.objects_for_user(user)) requested = set(collections_arg) return set(col for col in available if col.name in requested) def ping(request): Collection.objects.count() return HttpResponse('ok\n') def home(request): return render(request, 'home.html') @csrf_exempt def collections(request): return JsonResponse([ {'name': col.name, 'title': col.title, 'stats': col.get_meta()['stats']} for col in Collection.objects_for_user(request.user) ], safe=False) def _search(request, **kwargs): try: res, counts = es.search(**kwargs) res['status'] = 'ok' except es.SearchError as e: return JsonErrorResponse(e.reason) else: from .es import _index_id def col_name(id): return Collection.objects.get(id=id).name for item in res['hits']['hits']: name = col_name(_index_id(item['_index'])) url = 'doc/{}/{}'.format(name, item['_id']) item['_collection'] = name item['_url'] = request.build_absolute_uri(url) res['count_by_index'] = { col_name(i): counts[i] for i in counts } return JsonResponse(res) @csrf_exempt @limit_user def search(request): t0 = time() body = json.loads(request.body.decode('utf-8')) collections = collections_acl(request.user, body['collections']) success = False try: response = _search( request, from_=body.get('from', 0), size=body.get('size', 10), query=body['query'], _source=body.get('_source'), post_filter=body.get('post_filter'), sort=body.get('sort', ['_score']), aggs=body.get('aggs', {}), highlight=body.get('highlight'), collections=[c.name for c in collections], search_after=body.get('search_after'), ) success = True return response finally: signals.search.send('hoover.search', **{ 'request': request, 'collections': collections, 'duration': time() - t0, 'success': success, }) @limit_user def doc(request, collection_name, id, suffix): for collection in Collection.objects_for_user(request.user): if collection.name == collection_name: break else: raise Http404 t0 = time() success = False try: rv = collection.get_loader().get(id).view(request, suffix) success = True return rv finally: signals.doc.send('hoover.search', **{ 'request': request, 'collection': collection, 'doc_id': id, 'duration': time() - t0, 'success': success, }) def whoami(request): if settings.HOOVER_AUTHPROXY: logout_url = '/__auth/logout' else: logout_url = reverse('logout') + '?next=/' urls = { 'login': settings.LOGIN_URL, 'admin': reverse('admin:index'), 'logout': logout_url, 'hypothesis_embed': settings.HOOVER_HYPOTHESIS_EMBED, } try: password_change = reverse('password_change') except NoReverseMatch: pass else: urls['password_change'] = password_change return JsonResponse({ 'username': request.user.username, 'admin': request.user.is_superuser, 'urls': urls, 'title': settings.HOOVER_TITLE, }) @csrf_exempt @limit_user def batch(request): t0 = time() body = json.loads(request.body.decode('utf-8')) collections = collections_acl(request.user, body['collections']) query_strings = body.get('query_strings') aggs = body.get('aggs') if not collections: return JsonErrorResponse("No collections selected.") if not query_strings: return JsonErrorResponse("No items to be searched.") batch_limit = settings.HOOVER_BATCH_LIMIT if len(query_strings) > batch_limit: reason = "Too many queries. Limit is {}.".format(batch_limit) return JsonErrorResponse(reason) success = False try: res = es.batch_count( query_strings, collections, aggs ) res['status'] = 'ok' success = True return JsonResponse(res) except es.SearchError as e: return JsonErrorResponse(e.reason) finally: signals.batch.send('hoover.batch', **{ 'request': request, 'collections': collections, 'duration': time() - t0, 'success': success, 'query_count': len(query_strings), }) def is_staff(request): if request.user.is_staff: return JsonResponse({'is_staff': True}, status=200) else: return JsonResponse({'is_staff': False}, status=403) def limits(request): """ Get rate limits """ return JsonResponse({ 'batch': settings.HOOVER_BATCH_LIMIT, 'requests': get_request_limits(request.user), })