diff --git a/api/lightning/node.py b/api/lightning/node.py
index e95b9668..092db860 100644
--- a/api/lightning/node.py
+++ b/api/lightning/node.py
@@ -122,22 +122,22 @@ class LNNode():
lnpayment.save()
return True
- @classmethod
- def check_until_invoice_locked(cls, payment_hash, expiration):
- '''Checks until hold invoice is locked.
- When invoice is locked, returns true.
- If time expires, return False.'''
- # Experimental, might need asyncio. Best if subscribing all invoices and running a background task
- # Maybe best to pass LNpayment object and change status live.
+ # @classmethod
+ # def check_until_invoice_locked(cls, payment_hash, expiration):
+ # '''Checks until hold invoice is locked.
+ # When invoice is locked, returns true.
+ # If time expires, return False.'''
+ # # Experimental, might need asyncio. Best if subscribing all invoices and running a background task
+ # # Maybe best to pass LNpayment object and change status live.
- request = invoicesrpc.SubscribeSingleInvoiceRequest(r_hash=payment_hash)
- for invoice in cls.invoicesstub.SubscribeSingleInvoice(request):
- print(invoice)
- if timezone.now > expiration:
- break
- if invoice.state == 3: # True if hold invoice is accepted.
- return True
- return False
+ # request = invoicesrpc.SubscribeSingleInvoiceRequest(r_hash=payment_hash)
+ # for invoice in cls.invoicesstub.SubscribeSingleInvoice(request):
+ # print(invoice)
+ # if timezone.now > expiration:
+ # break
+ # if invoice.state == 3: # True if hold invoice is accepted.
+ # return True
+ # return False
@classmethod
diff --git a/api/logics.py b/api/logics.py
index 291cb572..85f5acda 100644
--- a/api/logics.py
+++ b/api/logics.py
@@ -50,9 +50,9 @@ class Logics():
def validate_order_size(order):
'''Validates if order is withing limits in satoshis at t0'''
if order.t0_satoshis > MAX_TRADE:
- return False, {'bad_request': 'Your order is too big. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now. But limit is '+'{:,}'.format(MAX_TRADE)+ ' Sats'}
+ return False, {'bad_request': 'Your order is too big. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now, but the limit is '+'{:,}'.format(MAX_TRADE)+ ' Sats'}
if order.t0_satoshis < MIN_TRADE:
- return False, {'bad_request': 'Your order is too small. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now. But limit is '+'{:,}'.format(MIN_TRADE)+ ' Sats'}
+ return False, {'bad_request': 'Your order is too small. It is worth '+'{:,}'.format(order.t0_satoshis)+' Sats now, but the limit is '+'{:,}'.format(MIN_TRADE)+ ' Sats'}
return True, None
@classmethod
@@ -386,11 +386,12 @@ class Logics():
return True, None
# 2) When maker cancels after bond
- '''The order dissapears from book and goes to cancelled. Maker is charged the bond to prevent DDOS
- on the LN node and order book. TODO Only charge a small part of the bond (requires maker submitting an invoice)'''
+ '''The order dissapears from book and goes to cancelled. If strict, maker is charged the bond
+ to prevent DDOS on the LN node and order book. If not strict, maker is returned
+ the bond (more user friendly).'''
elif order.status == Order.Status.PUB and order.maker == user:
#Settle the maker bond (Maker loses the bond for cancelling public order)
- if cls.settle_bond(order.maker_bond):
+ if cls.return_bond(order.maker_bond): # strict: cls.settle_bond(order.maker_bond):
order.status = Order.Status.UCA
order.save()
return True, None
diff --git a/api/views.py b/api/views.py
index 21b7b9b8..b4168400 100644
--- a/api/views.py
+++ b/api/views.py
@@ -39,6 +39,9 @@ class MakerView(CreateAPIView):
def post(self,request):
serializer = self.serializer_class(data=request.data)
+ if not request.user.is_authenticated:
+ return Response({'bad_request':'Woops! It seems you do not have a robot avatar'}, status.HTTP_400_BAD_REQUEST)
+
if not serializer.is_valid(): return Response(status=status.HTTP_400_BAD_REQUEST)
type = serializer.data.get('type')
@@ -413,15 +416,16 @@ class UserView(APIView):
def delete(self,request):
''' Pressing "give me another" deletes the logged in user '''
user = request.user
- if not user:
+ if not user.is_authenticated:
return Response(status.HTTP_403_FORBIDDEN)
- # Only delete if user life is shorter than 30 minutes. Helps deleting users by mistake
+ # Only delete if user life is shorter than 30 minutes. Helps to avoid deleting users by mistake
if user.date_joined < (timezone.now() - timedelta(minutes=30)):
return Response(status.HTTP_400_BAD_REQUEST)
# Check if it is not a maker or taker!
- if not Logics.validate_already_maker_or_taker(user):
+ not_participant, _, _ = Logics.validate_already_maker_or_taker(user)
+ if not not_participant:
return Response({'bad_request':'User cannot be deleted while he is part of an order'}, status.HTTP_400_BAD_REQUEST)
logout(request)
diff --git a/frontend/src/components/BookPage.js b/frontend/src/components/BookPage.js
index 37b5b903..6e1100c6 100644
--- a/frontend/src/components/BookPage.js
+++ b/frontend/src/components/BookPage.js
@@ -93,7 +93,7 @@ export default class BookPage extends Component {
renderCell: (params) => {return (
-
+
@@ -147,7 +147,7 @@ export default class BookPage extends Component {
{ field: 'robosat', headerName: 'Robot', width: 80,
renderCell: (params) => {return (
-
+
);
} },
diff --git a/frontend/src/components/BottomBar.js b/frontend/src/components/BottomBar.js
index 1cf718b5..b5c4cd91 100644
--- a/frontend/src/components/BottomBar.js
+++ b/frontend/src/components/BottomBar.js
@@ -17,6 +17,7 @@ import SendIcon from '@mui/icons-material/Send';
import PublicIcon from '@mui/icons-material/Public';
import NumbersIcon from '@mui/icons-material/Numbers';
import PasswordIcon from '@mui/icons-material/Password';
+import ContentCopy from "@mui/icons-material/ContentCopy";
// pretty numbers
function pn(x) {
@@ -198,7 +199,7 @@ export default class BottomBar extends Component {
-
-
+
{this.props.token ?
+ size='small'
+ InputProps={{
+ endAdornment:
+ navigator.clipboard.writeText(this.props.token)}>
+
+ ,
+ }}
+ />
:
'Cannot remember'}
@@ -258,7 +266,7 @@ bottomBarDesktop =()=>{
0 & !this.state.profileShown) ? "": null} color="primary">
-
@@ -462,7 +470,7 @@ bottomBarPhone =()=>{
0 & !this.state.profileShown) ? "1": null} color="primary">
-
diff --git a/frontend/src/components/OrderPage.js b/frontend/src/components/OrderPage.js
index e8f20b5b..8c712aee 100644
--- a/frontend/src/components/OrderPage.js
+++ b/frontend/src/components/OrderPage.js
@@ -309,7 +309,7 @@ export default class OrderPage extends Component {
// If maker and Waiting for Bond. Or if taker and Waiting for bond.
// Simply allow to cancel without showing the cancel dialog.
- if ((this.state.is_maker & this.state.status == 0) || this.state.is_taker & this.state.status == 3){
+ if ((this.state.is_maker & [0,1].includes(this.state.status)) || this.state.is_taker & this.state.status == 3){
return(
@@ -317,7 +317,7 @@ export default class OrderPage extends Component {
)}
// If the order does not yet have an escrow deposited. Show dialog
// to confirm forfeiting the bond
- if ([1,3,6,7].includes(this.state.status)){
+ if ([3,6,7].includes(this.state.status)){
return(
@@ -354,7 +354,7 @@ export default class OrderPage extends Component {
-
@@ -370,7 +370,7 @@ export default class OrderPage extends Component {
-
diff --git a/frontend/src/components/UserGenPage.js b/frontend/src/components/UserGenPage.js
index 5a82141a..8873ebd2 100644
--- a/frontend/src/components/UserGenPage.js
+++ b/frontend/src/components/UserGenPage.js
@@ -3,7 +3,7 @@ import { Button , Dialog, Grid, Typography, TextField, ButtonGroup, CircularProg
import { Link } from 'react-router-dom'
import Image from 'material-ui-image'
import InfoDialog from './InfoDialog'
-import PublishIcon from '@mui/icons-material/Publish';
+import SmartToyIcon from '@mui/icons-material/SmartToy';
import CasinoIcon from '@mui/icons-material/Casino';
import ContentCopy from "@mui/icons-material/ContentCopy";
@@ -161,14 +161,11 @@ export default class UserGenPage extends Component {
}
- navigator.clipboard.writeText(this.state.token)}>
-
-
- navigator.clipboard.writeText(this.state.token)}>
+
+ ,
+ endAdornment:
+ ,
+ }}
/>
-
-
-
diff --git a/frontend/static/css/index.css b/frontend/static/css/index.css
index 81e52a49..53be5963 100644
--- a/frontend/static/css/index.css
+++ b/frontend/static/css/index.css
@@ -39,7 +39,7 @@ body {
.profileNickname {
margin: 0;
- left: -22px;
+ left: -16px;
}
.newAvatar {
@@ -51,14 +51,19 @@ body {
width: 200px;
}
-.avatar {
+.profileAvatar {
border: 0.5px solid #555;
filter: drop-shadow(0.5px 0.5px 0.5px #000000);
left: 35px;
}
-.rotatedAvatar {
- transform: scaleX(-1);
+.smallAvatar {
border: 0.5px solid #555;
filter: drop-shadow(0.5px 0.5px 0.5px #000000);
+}
+
+.flippedSmallAvatar {
+ transform: scaleX(-1);
+ border: 0.3px solid #555;
+ filter: drop-shadow(0.5px 0.5px 0.5px #000000);
}
\ No newline at end of file
diff --git a/setup.md b/setup.md
index 2983156a..8bdb232e 100644
--- a/setup.md
+++ b/setup.md
@@ -133,4 +133,79 @@ Note we are using mostly MaterialUI V5 (@mui/material) but Image loading from V4
### Launch the React render
from frontend/ directory
-`npm run dev`
\ No newline at end of file
+`npm run dev`
+
+## Robosats background threads.
+
+There is 3 processes that run asynchronously: two admin commands and a celery beat scheduler.
+The celery worker will run the task of caching external API market prices and cleaning(deleting) the generated robots that were never used.
+`celery -A robosats worker --beat -l debug -S django`
+
+The admin commands are used to keep an eye on the state of LND hold invoices and check whether orders have expired
+```
+python3 manage.py follow_invoices
+python3 manage.py clean_order
+```
+
+It might be best to set up system services to continuously run these background processes.
+
+### Follow invoices admin command as system service
+
+Create `/etc/systemd/system/follow_invoices.service` and edit with:
+
+```
+[Unit]
+Description=RoboSats Follow LND Invoices
+After=lnd.service
+StartLimitIntervalSec=0
+
+[Service]
+WorkingDirectory=/home//robosats/
+StandardOutput=file:/home//robosats/follow_invoices.log
+StandardError=file:/home//robosats/follow_invoices.log
+Type=simple
+Restart=always
+RestartSec=1
+User=
+ExecStart=python3 manage.py follow_invoices
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Then launch it with
+
+```
+systemctl start follow_invoices
+systemctl enable follow_invoices
+```
+### Clean orders admin command as system service
+
+Create `/etc/systemd/system/clean_orders.service` and edit with (replace for your username):
+
+```
+[Unit]
+Description=RoboSats Clean Orders
+After=lnd.service
+StartLimitIntervalSec=0
+
+[Service]
+WorkingDirectory=/home//robosats/
+StandardOutput=file:/home//robosats/clean_orders.log
+StandardError=file:/home//robosats/clean_orders.log
+Type=simple
+Restart=always
+RestartSec=1
+User=
+ExecStart=python3 manage.py clean_orders
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Then launch it with
+
+```
+systemctl start clean_orders
+systemctl enable clean_orders
+```
\ No newline at end of file