robosats/api/views.py
2022-01-02 14:31:19 -08:00

159 lines
5.7 KiB
Python

from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.models import User
from .serializers import OrderSerializer, MakeOrderSerializer
from .models import Order
from .nick_generator.nick_generator import NickGenerator
from robohash import Robohash
from scipy.stats import entropy
from math import log2
import numpy as np
import hashlib
from pathlib import Path
# Create your views here.
class MakeOrder(APIView):
serializer_class = MakeOrderSerializer
def post(self,request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
otype = serializer.data.get('type')
currency = serializer.data.get('currency')
amount = serializer.data.get('amount')
payment_method = serializer.data.get('payment_method')
premium = serializer.data.get('premium')
satoshis = serializer.data.get('satoshis')
#################
# TODO
# query if the user is already a maker or taker, return error
# Creates a new order in db
order = Order(
type=otype,
currency=currency,
amount=amount,
payment_method=payment_method,
premium=premium,
satoshis=satoshis)
order.save()
if not serializer.is_valid():
return Response(status=status.HTTP_400_BAD_REQUEST)
return Response(OrderSerializer(order).data, status=status.HTTP_201_CREATED)
class OrderView(APIView):
serializer_class = OrderSerializer
lookup_url_kwarg = 'order_id'
def get(self, request, format=None):
order_id = request.GET.get(self.lookup_url_kwarg)
if order_id != None:
order = Order.objects.filter(id=order_id)
# check if exactly one order is found in the db
if len(order) == 1 :
print("It is only one!")
order = order[0]
data = self.serializer_class(order).data
# TODO
# # Check if requester is participant in the order and add boolean to response
# user = authenticate(username=username, password=password)
# data['is_participant'] = any(user.id == order.maker, user.id == order.taker)
# if data['is_participant']:
# return Response(data, status=status.HTTP_200_OK)
# else:
# # Non participants can't get access to the status or who is the taker
# data.pop(['status'],['taker'])
# return Response(data, status=status.HTTP_200_OK)
return Response(data, status=status.HTTP_200_OK)
return Response({'Order Not Found':'Invalid Order Id'},status=status.HTTP_404_NOT_FOUND)
return Response({'Bad Request':'Order ID parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
class UserGenerator(APIView):
lookup_url_kwarg = 'token'
NickGen = NickGenerator(
lang='English',
use_adv=False,
use_adj=True,
use_noun=True,
max_num=999)
def get(self,request):
'''
Get a new user derived from a high entropy token
- Request has a high-entropy token,
- Generates new nickname and avatar.
- Creates login credentials (new User object)
Response with Avatar and Nickname.
'''
token = request.GET.get(self.lookup_url_kwarg)
# Compute token entropy
value, counts = np.unique(list(token), return_counts=True)
shannon_entropy = entropy(counts, base=62)
bits_entropy = log2(len(value)**len(token))
# Start preparing payload
context = {'token_shannon_entropy': shannon_entropy, 'token_bits_entropy': bits_entropy}
# Deny user gen if entropy below 128 bits or 0.7 shannon heterogeneity
if bits_entropy < 128 or shannon_entropy < 0.7:
context['bad_request'] = 'The token does not have enough entropy'
return Response(context, status=status.HTTP_400_BAD_REQUEST)
# Hashes the token, only 1 iteration. Maybe more is better.
hash = hashlib.sha256(str.encode(token)).hexdigest()
# generate nickname
nickname = self.NickGen.short_from_SHA256(hash, max_length=18)[0]
context['nickname'] = nickname
# generate avatar
rh = Robohash(hash)
rh.assemble(roboset='set1') # bgset='any' for backgrounds ON
avatars_path = Path('frontend/static/assets/avatars')
avatars_path.mkdir(parents=True, exist_ok=True)
with open(avatars_path.joinpath(nickname+".png"), "wb") as f:
rh.img.save(f, format="png")
# Create new credentials if nickname is new
if len(User.objects.filter(username=nickname)) == 0:
User.objects.create_user(username=nickname, password=token, is_staff=False)
else:
## TODO only report a match was found if it has
## been at least 30 minutes since user creation
## Why: frontend gets confused to say Welcome back too soon
context['found'] = 'A matching nickname was found'
# TODO, "A matching nickname was found, but it is not yours!"
# why? It is unlikely but there is only 20 billion names
# but if the token is not exact
# TODO Keep user authenticated.
# BaseBackend.authenticate(self, request=None,username=nickname, password=token)
return Response(context, status=status.HTTP_201_CREATED)