diff --git a/.gitignore b/.gitignore index 7d847cc..f97c19d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__/ .env .vs/ .venv/ +data/ \ No newline at end of file diff --git a/FireSales.bsdesign b/FireSales.bsdesign index 9e29138..a4ac4a4 100644 Binary files a/FireSales.bsdesign and b/FireSales.bsdesign differ diff --git a/data/listings.json b/data/listings.json deleted file mode 100644 index db342aa..0000000 --- a/data/listings.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "domain": "woodburn43", - "description": "This is a test listing update", - "price": 100.2, - "tx": "somelonghexstring", - "updated": 1738214534 - } -] \ No newline at end of file diff --git a/requests/accept.hurl b/requests/accept.hurl new file mode 100644 index 0000000..e69de29 diff --git a/requests/getMessage.hurl b/requests/getMessage.hurl new file mode 100644 index 0000000..2b52abb --- /dev/null +++ b/requests/getMessage.hurl @@ -0,0 +1 @@ +GET http://127.0.0.1:5000/api/v1/listing-message?domain=woodburn43&description=This%20is%20a%20test%20listing%20update&price=100.2&contact=contact%40nathan.woodburn.au \ No newline at end of file diff --git a/requests/list.hurl b/requests/list.hurl index 858ffd3..f16716b 100644 --- a/requests/list.hurl +++ b/requests/list.hurl @@ -3,5 +3,6 @@ POST http://127.0.0.1:5000/api/v1/list "domain":"woodburn43", "price":100.2, "description":"This is a test listing update", -"tx":"somelonghexstring" +"contact":"contact@nathan.woodburn.au", +"signature":"az51SGj9/7MjQ8Xl6LNM5Mu99FwEna8LTDkckWqH+9wZ6L5YQNV3vEyQIVI1BApS7oI8efgjE+edXHkXO4o7uw==" } \ No newline at end of file diff --git a/sales.py b/sales.py index d70ce35..26be029 100644 --- a/sales.py +++ b/sales.py @@ -19,7 +19,8 @@ class Listing: self.domain = '' self.price = 0 self.description = '' - self.tx = '' + self.contact = '' + self.signature = '' self.updated = -1 for key, value in kwargs.items(): @@ -28,16 +29,20 @@ class Listing: self.updated = int(time.time()) def __str__(self): - return f"Listing of {self.domain} for {self.price}, Description: {self.description}, Contact: {self.contact}, Signed: {self.signed}, Signature: {self.signature}, Updated: {self.updated}" + return f"Domain: {self.domain}\nPrice: {self.price}\nDescription: {self.description}\nContact: {self.contact}" + + def getMessage(self): + return str(self).encode('utf-8').hex() def toHTML(self): return f""" -
+
-

{self.domain}/

+

{self.domain}/

{self.price} HNS

{self.description}

-

TX: {self.tx}

+

Contact: {self.contact}

+ Delete Listing
""" @@ -47,12 +52,13 @@ class Listing: 'domain': self.domain, 'description': self.description, 'price': self.price, - 'tx': self.tx, + 'contact': self.contact, + 'signature': self.signature, 'updated': self.updated } - - def to_dict(self): - return self.toJSON() + + def signed(self): + return validate_signature(self.domain,self.signature,self.getMessage()) def txValid(self): # TODO Validate tx is valid diff --git a/server.py b/server.py index f600932..ea6de75 100644 --- a/server.py +++ b/server.py @@ -85,6 +85,42 @@ def view(domain: str): return render_template("404.html"), 404 return render_template("view.html", listing=listing) +@app.route("/list", methods=["POST"]) +def list_form(): + # Get form data + data = request.form + domain = data['domain'] + price = data['price'] + description = data['description'] + contact = data['contact'] + signature = data['signature'] + + listing = sales.Listing(domain=domain,price=price,description=description,contact=contact,signature=signature) + if not listing.signed(): + return render_template("list.html", domain=domain, price=price, description=description, contact=contact, signature=signature, error="Signature is not valid") + + status = sales.add_listing(listing) + if status != True: + return render_template("list.html", domain=domain, price=price, description=description, contact=contact, signature=signature, error="Failed to add listing") + return render_template("list.html", success="Successfully added listing") + +@app.route("/delete/") +def delete_web(domain: str): + message = f"FS: {domain}" + return render_template("delete.html", domain=domain, message=message) + +@app.route("/delete/", methods=["POST"]) +def delete_form(domain:str): + data = request.form + signature = data['signature'] + + + if not sales.validate_cancel_signature(domain,signature): + return render_template("delete.html", domain=domain, error="Invalid signature") + + sales.remove_listing(domain) + return redirect("/") + @app.route("/view/", methods=["POST"]) def post_offer(domain: str): data = request.form @@ -101,24 +137,46 @@ def report(domain: str): return render_template("404.html"), 404 return redirect("https://l.woodburn.au/contact") +@app.route("/verify/") +def verify(domain: str): + listing = sales.search_listings(domain) + if not listing: + return render_template("404.html"), 404 + return render_template("verify.html", domain=domain, string=str(listing).replace('\n','
'), message=listing.getMessage(), signature=listing.signature) #region API Routes def validate_data(data,required): for key in required: if key not in data: - return jsonify({'error': f'Missing {key}','success': False}) + return jsonify({'error': f'Missing {key}','success': False}),400 return True +@app.route("/api/v1/listing-message", methods=["GET"]) +def listingMessage(): + # Create listing + valid = validate_data(request.args,['domain','description','price','contact']) + if valid != True: + return valid + + listing = sales.Listing(**request.args) + return { + 'message': listing.getMessage(), + 'success': True + } + @app.route("/api/v1/list", methods=["POST"]) def list(): data = request.get_json() # Validate data has domain,description,price,contact - valid = validate_data(data,['domain','description','price','tx']) + valid = validate_data(data,['domain','description','price','contact']) if valid != True: return valid listing = sales.Listing(**data) + if not listing.signed(): + return jsonify({'error': 'Not signed','success': False,'message': f'Sign this message to post listing: {listing.getMessage()}'}) + status = sales.add_listing(listing) if status != True: return jsonify({'error': 'Failed to add listing','success': False,'message': status}) diff --git a/templates/delete.html b/templates/delete.html new file mode 100644 index 0000000..d497c0e --- /dev/null +++ b/templates/delete.html @@ -0,0 +1,63 @@ + + + + + + + FireSales + + + + + + + + + + + + + + + +
+
+
+
+
+

FireSales

+

Self custodial domain sales

+
+
+
+
+

{{error}}

+

{{success}}

+
+
+
+
+

Delete listing for {{domain}}

+

Message to sign: {{message}}

+
+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index da8387d..d36589b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -20,10 +20,11 @@