Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
812fc84d3e
|
|||
|
6d318a597b
|
|||
|
83bd6b9643
|
|||
|
c93b2652f5
|
|||
|
86e174c337
|
|||
|
e7b787c30b
|
|||
|
997828795a
|
|||
|
30de2d585e
|
|||
|
56eabfc1fc
|
|||
|
e0f24267f5
|
|||
|
2d51882d20
|
|||
|
06b1eea9ef
|
|||
|
d483cfdcfd
|
|||
|
46ed0173d3
|
|||
|
9dd50d1292
|
|||
|
53148f573e
|
|||
|
e8f052e0d1
|
|||
|
7f450d620a
|
|||
|
41a1bc743f
|
|||
|
30108e3bc5
|
|||
|
a2dc9f43e3
|
32
.dockerignore
Normal file
32
.dockerignore
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
.env
|
||||||
|
.env*
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
templates/assets/css/styles.min.css
|
||||||
|
|
||||||
|
ignore/
|
||||||
|
|
||||||
|
plugins/signatures.json
|
||||||
|
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
user_data/
|
||||||
|
customPlugins/
|
||||||
|
cache/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
hsd/
|
||||||
|
hsd-data/
|
||||||
|
hsd.lock
|
||||||
|
hsdconfig.json
|
||||||
|
|
||||||
|
Dockerfile
|
||||||
|
Dockerfile.hsd
|
||||||
|
FireWalletBrowser.bsdesign
|
||||||
|
LICENSE.md
|
||||||
|
README.md
|
||||||
|
docker-compose.yml
|
||||||
|
example.env
|
||||||
|
plugins.md
|
||||||
|
|
||||||
@@ -4,11 +4,22 @@ on:
|
|||||||
push:
|
push:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build Image:
|
Build Images:
|
||||||
runs-on: [ubuntu-latest, amd]
|
runs-on: [ubuntu-latest, amd]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
variant:
|
||||||
|
- target: default
|
||||||
|
tag_suffix: ""
|
||||||
|
dockerfile: "Dockerfile"
|
||||||
|
- target: hsd
|
||||||
|
tag_suffix: "-hsd"
|
||||||
|
dockerfile: "Dockerfile.hsd"
|
||||||
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Install Docker
|
- name: Install Docker
|
||||||
run : |
|
run : |
|
||||||
apt-get install ca-certificates curl gnupg
|
apt-get install ca-certificates curl gnupg
|
||||||
@@ -34,8 +45,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
docker build -t firewallet:$tag_num .
|
docker build --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') --build-arg VCS_REF=$GITEA_SHA --build-arg VERSION=$GITEA_TAG --file ${{matrix.variant.dockerfile}} -t firewallet${{matrix.variant.tag_suffix}}:$tag_num .
|
||||||
docker tag firewallet:$tag_num git.woodburn.au/nathanwoodburn/firewallet:$tag_num
|
docker tag firewallet${{matrix.variant.tag_suffix}}:$tag_num git.woodburn.au/nathanwoodburn/firewallet${{matrix.variant.tag_suffix}}:$tag_num
|
||||||
docker push git.woodburn.au/nathanwoodburn/firewallet:$tag_num
|
docker push git.woodburn.au/nathanwoodburn/firewallet${{matrix.variant.tag_suffix}}:$tag_num
|
||||||
docker tag firewallet:$tag_num git.woodburn.au/nathanwoodburn/firewallet:$tag
|
docker tag firewallet${{matrix.variant.tag_suffix}}:$tag_num git.woodburn.au/nathanwoodburn/firewallet${{matrix.variant.tag_suffix}}:$tag
|
||||||
docker push git.woodburn.au/nathanwoodburn/firewallet:$tag
|
docker push git.woodburn.au/nathanwoodburn/firewallet${{matrix.variant.tag_suffix}}:$tag
|
||||||
40
.gitea/workflows/test.yml
Normal file
40
.gitea/workflows/test.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: Tests and Linting
|
||||||
|
run-name: Python Compatibility and Linting tests
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Tests-Linting:
|
||||||
|
runs-on: [ubuntu-latest, amd]
|
||||||
|
container: catthehacker/ubuntu:act-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.10', '3.11', '3.13']
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
if [ -f requirements.txt ]; then
|
||||||
|
pip install -r requirements.txt
|
||||||
|
fi
|
||||||
|
pip install pytest ruff
|
||||||
|
|
||||||
|
- name: Test with pytest
|
||||||
|
run: |
|
||||||
|
echo "Testing with Python ${{ matrix.python-version }}"
|
||||||
|
python -m pytest main.py
|
||||||
|
|
||||||
|
- name: Lint with ruff
|
||||||
|
run: |
|
||||||
|
echo "Linting with Python ${{ matrix.python-version }}"
|
||||||
|
ruff check
|
||||||
14
Dockerfile
14
Dockerfile
@@ -9,7 +9,19 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
|||||||
COPY . /app
|
COPY . /app
|
||||||
|
|
||||||
# Add mount point for data volume
|
# Add mount point for data volume
|
||||||
# VOLUME /data
|
VOLUME /app/user_data
|
||||||
|
|
||||||
|
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG VCS_REF
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="FireWallet" \
|
||||||
|
org.opencontainers.image.description="The Handshake Wallet That is Fire" \
|
||||||
|
org.opencontainers.image.url="https://firewallet.au" \
|
||||||
|
org.opencontainers.image.source="https://git.woodburn.au/nathanwoodburn/firewalletbrowser" \
|
||||||
|
org.opencontainers.image.version="2.0.0" \
|
||||||
|
org.opencontainers.image.created=$BUILD_DATE \
|
||||||
|
org.opencontainers.image.licenses="AGPL-3.0-only"
|
||||||
|
|
||||||
ENTRYPOINT ["python3"]
|
ENTRYPOINT ["python3"]
|
||||||
CMD ["server.py"]
|
CMD ["server.py"]
|
||||||
|
|||||||
57
Dockerfile.hsd
Normal file
57
Dockerfile.hsd
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# ---- HSD build stage ----
|
||||||
|
FROM node:22-alpine AS hsd-build
|
||||||
|
WORKDIR /opt/hsd
|
||||||
|
RUN apk add --no-cache git bash unbound-dev gmp-dev g++ gcc make python3
|
||||||
|
RUN git clone --depth=1 --branch v8.0.0 https://github.com/handshake-org/hsd.git .
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
|
||||||
|
# ---- Final stage ----
|
||||||
|
FROM python:3.13-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install runtime deps only
|
||||||
|
RUN apk add --no-cache unbound-dev gmp
|
||||||
|
|
||||||
|
|
||||||
|
# Copy node and npm from hsd-build stage
|
||||||
|
COPY --from=hsd-build /usr/local/bin/node /usr/local/bin/node
|
||||||
|
COPY --from=hsd-build /usr/local/lib/node_modules/npm /usr/local/lib/node
|
||||||
|
COPY --from=hsd-build /usr/local/bin/npm /usr/local/bin/npm
|
||||||
|
|
||||||
|
# Copy FireWallet dependencies
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copy HSD from build stage
|
||||||
|
COPY --from=hsd-build /opt/hsd /app/hsd
|
||||||
|
|
||||||
|
# Copy FireWallet source
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose ports
|
||||||
|
EXPOSE 5000
|
||||||
|
# Optional HSD ports
|
||||||
|
# EXPOSE 12037
|
||||||
|
# EXPOSE 12039
|
||||||
|
|
||||||
|
ENV INTERNAL_HSD=true
|
||||||
|
ENV HSD_DOCKER_CONTAINER=true
|
||||||
|
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG VCS_REF
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.title="FireWallet (HSD)" \
|
||||||
|
org.opencontainers.image.description="The Handshake Wallet That is Fire" \
|
||||||
|
org.opencontainers.image.url="https://firewallet.au" \
|
||||||
|
org.opencontainers.image.source="https://git.woodburn.au/nathanwoodburn/firewalletbrowser" \
|
||||||
|
org.opencontainers.image.version="2.0.0" \
|
||||||
|
org.opencontainers.image.created=$BUILD_DATE \
|
||||||
|
org.opencontainers.image.licenses="AGPL-3.0-only"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
VOLUME ["/app/hsd-data", "/app/user_data"]
|
||||||
|
|
||||||
|
|
||||||
|
ENTRYPOINT ["python3"]
|
||||||
|
CMD ["server.py"]
|
||||||
Binary file not shown.
114
account.py
114
account.py
@@ -13,7 +13,6 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
@@ -131,7 +130,7 @@ def check_password(cookie: str|None, password: str|None):
|
|||||||
password = ""
|
password = ""
|
||||||
|
|
||||||
account = check_account(cookie)
|
account = check_account(cookie)
|
||||||
if account == False:
|
if not account:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check if the password is valid
|
# Check if the password is valid
|
||||||
@@ -639,7 +638,7 @@ def check_address(address: str, allow_name: bool = True, return_address: bool =
|
|||||||
return False
|
return False
|
||||||
return 'Invalid address'
|
return 'Invalid address'
|
||||||
|
|
||||||
if response['result']['isvalid'] == True:
|
if response['result']['isvalid']:
|
||||||
if return_address:
|
if return_address:
|
||||||
return address
|
return address
|
||||||
return 'Valid address'
|
return 'Valid address'
|
||||||
@@ -789,7 +788,7 @@ def getAddressFromCoin(coinhash: str, coinindex = 0):
|
|||||||
# Get the address from the hash
|
# Get the address from the hash
|
||||||
response = requests.get(get_node_api_url(f"coin/{coinhash}/{coinindex}"))
|
response = requests.get(get_node_api_url(f"coin/{coinhash}/{coinindex}"))
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
print(f"Error getting address from coin")
|
print("Error getting address from coin")
|
||||||
return "No Owner"
|
return "No Owner"
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if 'address' not in data:
|
if 'address' not in data:
|
||||||
@@ -802,7 +801,7 @@ def renewDomain(account, domain):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -835,7 +834,7 @@ def getDNS(domain: str):
|
|||||||
return {
|
return {
|
||||||
"error": "No DNS records"
|
"error": "No DNS records"
|
||||||
}
|
}
|
||||||
if response['result'] == None:
|
if response['result'] is None:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if 'records' not in response['result']:
|
if 'records' not in response['result']:
|
||||||
@@ -847,7 +846,7 @@ def setDNS(account, domain, records):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -919,7 +918,7 @@ def getNodeSync():
|
|||||||
|
|
||||||
def getWalletStatus():
|
def getWalletStatus():
|
||||||
response = hsw.rpc_getWalletInfo()
|
response = hsw.rpc_getWalletInfo()
|
||||||
if 'error' in response and response['error'] != None:
|
if 'error' in response and response['error'] is not None:
|
||||||
return "Error"
|
return "Error"
|
||||||
|
|
||||||
# return response
|
# return response
|
||||||
@@ -968,7 +967,7 @@ def getPendingReveals(account):
|
|||||||
if bid['name'] == domain['name']:
|
if bid['name'] == domain['name']:
|
||||||
state_found = False
|
state_found = False
|
||||||
for reveal in reveals:
|
for reveal in reveals:
|
||||||
if reveal['own'] == True:
|
if reveal['own']:
|
||||||
if bid['value'] == reveal['value']:
|
if bid['value'] == reveal['value']:
|
||||||
state_found = True
|
state_found = True
|
||||||
|
|
||||||
@@ -998,8 +997,8 @@ def getPendingRedeems(account, password):
|
|||||||
pending.append(nameHash)
|
pending.append(nameHash)
|
||||||
else:
|
else:
|
||||||
pending.append(name['result'])
|
pending.append(name['result'])
|
||||||
except:
|
except Exception as e:
|
||||||
print("Failed to parse redeems")
|
print(f"Failed to parse redeems: {str(e)}")
|
||||||
|
|
||||||
return pending
|
return pending
|
||||||
|
|
||||||
@@ -1009,7 +1008,7 @@ def getPendingRegisters(account):
|
|||||||
domains = getDomains(account, False)
|
domains = getDomains(account, False)
|
||||||
pending = []
|
pending = []
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
if domain['state'] == "CLOSED" and domain['registered'] == False:
|
if domain['state'] == "CLOSED" and not domain['registered']:
|
||||||
for bid in bids:
|
for bid in bids:
|
||||||
if bid['name'] == domain['name']:
|
if bid['name'] == domain['name']:
|
||||||
if bid['value'] == domain['highest']:
|
if bid['value'] == domain['highest']:
|
||||||
@@ -1027,9 +1026,9 @@ def getPendingFinalizes(account, password):
|
|||||||
pending = []
|
pending = []
|
||||||
try:
|
try:
|
||||||
for output in tx['outputs']:
|
for output in tx['outputs']:
|
||||||
if type(output) != dict:
|
if type(output) is not dict:
|
||||||
continue
|
continue
|
||||||
if not 'covenant' in output:
|
if 'covenant' not in output:
|
||||||
continue
|
continue
|
||||||
if output['covenant'].get("type") != 10:
|
if output['covenant'].get("type") != 10:
|
||||||
continue
|
continue
|
||||||
@@ -1042,8 +1041,8 @@ def getPendingFinalizes(account, password):
|
|||||||
pending.append(nameHash)
|
pending.append(nameHash)
|
||||||
else:
|
else:
|
||||||
pending.append(name['result'])
|
pending.append(name['result'])
|
||||||
except:
|
except Exception as e:
|
||||||
print("Failed to parse finalizes")
|
print(f"Failed to parse finalizes: {str(e)}")
|
||||||
return pending
|
return pending
|
||||||
|
|
||||||
|
|
||||||
@@ -1066,7 +1065,7 @@ def revealAuction(account, domain):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1086,7 +1085,7 @@ def revealAll(account):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1120,7 +1119,7 @@ def redeemAll(account):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1152,9 +1151,8 @@ def redeemAll(account):
|
|||||||
|
|
||||||
def registerAll(account):
|
def registerAll(account):
|
||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1177,9 +1175,8 @@ def registerAll(account):
|
|||||||
|
|
||||||
def finalizeAll(account):
|
def finalizeAll(account):
|
||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1212,7 +1209,7 @@ def bid(account, domain, bid, blind):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1237,7 +1234,7 @@ def openAuction(account, domain):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1259,7 +1256,7 @@ def transfer(account, domain, address):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1281,7 +1278,7 @@ def finalize(account, domain):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1318,7 +1315,7 @@ def cancelTransfer(account, domain):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1355,7 +1352,7 @@ def revoke(account, domain):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1392,7 +1389,7 @@ def sendBatch(account, batch):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1441,7 +1438,7 @@ def createBatch(account, batch):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1585,12 +1582,10 @@ def resendTXs():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def zapTXs(account):
|
def zapTXs(account, age=1200):
|
||||||
age = 60 * 20 # 20 minutes
|
|
||||||
|
|
||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1616,7 +1611,7 @@ def getxPub(account):
|
|||||||
if account.count(":") > 0:
|
if account.count(":") > 0:
|
||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1644,7 +1639,7 @@ def signMessage(account, domain, message):
|
|||||||
account_name = check_account(account)
|
account_name = check_account(account)
|
||||||
password = ":".join(account.split(":")[1:])
|
password = ":".join(account.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
@@ -1684,6 +1679,7 @@ def verifyMessageWithName(domain, signature, message):
|
|||||||
return response['result']
|
return response['result']
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"Error verifying message with name: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -1694,6 +1690,7 @@ def verifyMessage(address, signature, message):
|
|||||||
return response['result']
|
return response['result']
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(f"Error verifying message: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
@@ -1740,7 +1737,7 @@ def get_node_api_url(path=''):
|
|||||||
base_url = f"http://x:{HSD_API}@{HSD_IP}:{HSD_NODE_PORT}"
|
base_url = f"http://x:{HSD_API}@{HSD_IP}:{HSD_NODE_PORT}"
|
||||||
if isSPV() and any(path.startswith(route) for route in SPV_EXTERNAL_ROUTES):
|
if isSPV() and any(path.startswith(route) for route in SPV_EXTERNAL_ROUTES):
|
||||||
# If in SPV mode and the path is one of the external routes, use the external API
|
# If in SPV mode and the path is one of the external routes, use the external API
|
||||||
base_url = f"https://hsd.hns.au/api/v1"
|
base_url = "https://hsd.hns.au/api/v1"
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
# Ensure path starts with a slash if it's not empty
|
# Ensure path starts with a slash if it's not empty
|
||||||
@@ -1787,39 +1784,37 @@ def checkPreRequisites() -> dict[str, bool]:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Check if node is installed and get version
|
# Check if node is installed and get version
|
||||||
nodeSubprocess = subprocess.run(["node", "-v"], capture_output=True, text=True)
|
nodeSubprocess = subprocess.run(["node", "-v"], capture_output=True, text=True,timeout=2)
|
||||||
if nodeSubprocess.returncode == 0:
|
if nodeSubprocess.returncode == 0:
|
||||||
major_version = int(nodeSubprocess.stdout.strip().lstrip('v').split('.')[0])
|
major_version = int(nodeSubprocess.stdout.strip().lstrip('v').split('.')[0])
|
||||||
if major_version >= HSD_CONFIG.get("minNodeVersion", 20):
|
if major_version >= HSD_CONFIG.get("minNodeVersion", 20):
|
||||||
prerequisites["node"] = True
|
prerequisites["node"] = True
|
||||||
except FileNotFoundError:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Check if npm is installed
|
# Check if npm is installed
|
||||||
npmSubprocess = subprocess.run(["npm", "-v"], capture_output=True, text=True)
|
npmSubprocess = subprocess.run(["npm", "-v"], capture_output=True, text=True,timeout=2)
|
||||||
if npmSubprocess.returncode == 0:
|
if npmSubprocess.returncode == 0:
|
||||||
major_version = int(npmSubprocess.stdout.strip().split('.')[0])
|
major_version = int(npmSubprocess.stdout.strip().split('.')[0])
|
||||||
if major_version >= HSD_CONFIG.get("minNPMVersion", 8):
|
if major_version >= HSD_CONFIG.get("minNPMVersion", 8):
|
||||||
prerequisites["npm"] = True
|
prerequisites["npm"] = True
|
||||||
except FileNotFoundError:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Check if git is installed
|
# Check if git is installed
|
||||||
gitSubprocess = subprocess.run(["git", "-v"], capture_output=True, text=True)
|
gitSubprocess = subprocess.run(["git", "-v"], capture_output=True, text=True,timeout=2)
|
||||||
if gitSubprocess.returncode == 0:
|
if gitSubprocess.returncode == 0:
|
||||||
prerequisites["git"] = True
|
prerequisites["git"] = True
|
||||||
except FileNotFoundError:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Check if hsd is installed
|
# Check if hsd is installed
|
||||||
if os.path.exists("./hsd/bin/hsd"):
|
if os.path.exists("./hsd/bin/hsd"):
|
||||||
prerequisites["hsd"] = True
|
prerequisites["hsd"] = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return prerequisites
|
return prerequisites
|
||||||
|
|
||||||
|
|
||||||
@@ -1827,11 +1822,23 @@ def checkPreRequisites() -> dict[str, bool]:
|
|||||||
def hsdInit():
|
def hsdInit():
|
||||||
if not HSD_INTERNAL_NODE:
|
if not HSD_INTERNAL_NODE:
|
||||||
return
|
return
|
||||||
prerequisites = checkPreRequisites()
|
|
||||||
|
|
||||||
|
# Don't check prerequisites if HSD is included in a docker container
|
||||||
|
if os.getenv("HSD_DOCKER_CONTAINER", "false").lower() == "true":
|
||||||
|
prerequisites = {
|
||||||
|
"node": True,
|
||||||
|
"npm": True,
|
||||||
|
"git": True,
|
||||||
|
"hsd": True
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
prerequisites = checkPreRequisites()
|
||||||
|
|
||||||
|
minNodeVersion = HSD_CONFIG.get("minNodeVersion", 20)
|
||||||
|
minNPMVersion = HSD_CONFIG.get("minNPMVersion", 8)
|
||||||
PREREQ_MESSAGES = {
|
PREREQ_MESSAGES = {
|
||||||
"node": "Install Node.js from https://nodejs.org/en/download (Version >= {minNodeVersion})",
|
"node": f"Install Node.js from https://nodejs.org/en/download (Version >= {minNodeVersion})",
|
||||||
"npm": "Install npm (version >= {minNPMVersion}) - usually comes with Node.js",
|
"npm": f"Install npm (version >= {minNPMVersion}) - usually comes with Node.js",
|
||||||
"git": "Install Git from https://git-scm.com/downloads"}
|
"git": "Install Git from https://git-scm.com/downloads"}
|
||||||
|
|
||||||
|
|
||||||
@@ -1840,7 +1847,9 @@ def hsdInit():
|
|||||||
print("HSD Internal Node prerequisites not met:")
|
print("HSD Internal Node prerequisites not met:")
|
||||||
for key, value in prerequisites.items():
|
for key, value in prerequisites.items():
|
||||||
if not value:
|
if not value:
|
||||||
print(f" - {key} is missing or does not meet the version requirement.")
|
print(f" - {key} is missing or does not meet the version requirement.",flush=True)
|
||||||
|
if key in PREREQ_MESSAGES:
|
||||||
|
print(PREREQ_MESSAGES[key],flush=True)
|
||||||
exit(1)
|
exit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1934,7 +1943,8 @@ def hsdStart():
|
|||||||
try:
|
try:
|
||||||
signal.signal(signal.SIGINT, lambda s, f: (hsdStop(), sys.exit(0)))
|
signal.signal(signal.SIGINT, lambda s, f: (hsdStop(), sys.exit(0)))
|
||||||
signal.signal(signal.SIGTERM, lambda s, f: (hsdStop(), sys.exit(0)))
|
signal.signal(signal.SIGTERM, lambda s, f: (hsdStop(), sys.exit(0)))
|
||||||
except:
|
except Exception as e:
|
||||||
|
print(f"Failed to set signal handlers: {str(e)}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
docker-compose-internal.yml
Normal file
14
docker-compose-internal.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
services:
|
||||||
|
firewallet:
|
||||||
|
image: git.woodburn.au/nathanwoodburn/firewallet-hsd:latest
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
volumes:
|
||||||
|
- hsd_data:/app/hsd-data
|
||||||
|
- user_data:/app/user_data
|
||||||
|
environment:
|
||||||
|
- INTERNAL_HSD=true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
hsd_data:
|
||||||
|
user_data:
|
||||||
26
docker-compose.yml
Normal file
26
docker-compose.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
hsd:
|
||||||
|
image: ghcr.io/handshake-org/hsd:8
|
||||||
|
volumes:
|
||||||
|
- hsd_data:/root/.hsd
|
||||||
|
environment:
|
||||||
|
- HSD_HTTP_HOST=0.0.0.0
|
||||||
|
- HSD_WALLET_HTTP_HOST=0.0.0.0
|
||||||
|
- HSD_LOG_LEVEL=error
|
||||||
|
- HSD_API_KEY=changeme
|
||||||
|
|
||||||
|
|
||||||
|
firewallet:
|
||||||
|
image: git.woodburn.au/nathanwoodburn/firewallet:latest
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
environment:
|
||||||
|
- HSD_IP=hsd
|
||||||
|
- HSD_API=changeme
|
||||||
|
volumes:
|
||||||
|
- user_data:/app/user_data
|
||||||
|
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
hsd_data:
|
||||||
|
user_data:
|
||||||
@@ -11,7 +11,6 @@ import dns.query
|
|||||||
import dns.rdatatype
|
import dns.rdatatype
|
||||||
import httpx
|
import httpx
|
||||||
from requests_doh import DNSOverHTTPSSession, add_dns_provider
|
from requests_doh import DNSOverHTTPSSession, add_dns_provider
|
||||||
import requests
|
|
||||||
import urllib3
|
import urllib3
|
||||||
from cryptography.x509.oid import ExtensionOID
|
from cryptography.x509.oid import ExtensionOID
|
||||||
|
|
||||||
@@ -172,11 +171,11 @@ def resolve_TLSA_with_doh(query_name, doh_url="https://hnsdoh.com/dns-query"):
|
|||||||
def emoji_to_punycode(emoji):
|
def emoji_to_punycode(emoji):
|
||||||
try:
|
try:
|
||||||
return emoji.encode("idna").decode("ascii")
|
return emoji.encode("idna").decode("ascii")
|
||||||
except Exception as e:
|
except Exception:
|
||||||
return emoji
|
return emoji
|
||||||
|
|
||||||
def punycode_to_emoji(punycode):
|
def punycode_to_emoji(punycode):
|
||||||
try:
|
try:
|
||||||
return punycode.encode("ascii").decode("idna")
|
return punycode.encode("ascii").decode("idna")
|
||||||
except Exception as e:
|
except Exception:
|
||||||
return punycode
|
return punycode
|
||||||
136
main.py
136
main.py
@@ -12,11 +12,9 @@ import re
|
|||||||
from flask_qrcode import QRcode
|
from flask_qrcode import QRcode
|
||||||
import domainLookup
|
import domainLookup
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import importlib
|
|
||||||
import plugin as plugins_module
|
import plugin as plugins_module
|
||||||
import gitinfo
|
import gitinfo
|
||||||
import datetime
|
import datetime
|
||||||
import functools
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
dotenv.load_dotenv()
|
||||||
@@ -30,7 +28,7 @@ fees = 0.02
|
|||||||
revokeCheck = random.randint(100000,999999)
|
revokeCheck = random.randint(100000,999999)
|
||||||
|
|
||||||
|
|
||||||
THEME = os.getenv("THEME")
|
THEME = os.getenv("THEME", "black")
|
||||||
|
|
||||||
|
|
||||||
def blocks_to_time(blocks: int) -> str:
|
def blocks_to_time(blocks: int) -> str:
|
||||||
@@ -114,7 +112,7 @@ def transactions():
|
|||||||
page = request.args.get('page', 1)
|
page = request.args.get('page', 1)
|
||||||
try:
|
try:
|
||||||
page = int(page)
|
page = int(page)
|
||||||
except:
|
except ValueError:
|
||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
if page < 1:
|
if page < 1:
|
||||||
@@ -196,7 +194,7 @@ def send():
|
|||||||
content = f"Are you sure you want to send {amount} HNS to {toAddress}<br><br>"
|
content = f"Are you sure you want to send {amount} HNS to {toAddress}<br><br>"
|
||||||
content += f"This will cost {amount} HNS + mining fees and is not able to be undone."
|
content += f"This will cost {amount} HNS + mining fees and is not able to be undone."
|
||||||
|
|
||||||
cancel = f"/send"
|
cancel = "/send"
|
||||||
confirm = f"/send/confirm?address={address}&amount={amount}"
|
confirm = f"/send/confirm?address={address}&amount={amount}"
|
||||||
|
|
||||||
|
|
||||||
@@ -211,7 +209,7 @@ def sendConfirmed():
|
|||||||
address = request.args.get("address")
|
address = request.args.get("address")
|
||||||
amount = float(request.args.get("amount","0"))
|
amount = float(request.args.get("amount","0"))
|
||||||
response = account_module.send(request.cookies.get("account"),address,amount)
|
response = account_module.send(request.cookies.get("account"),address,amount)
|
||||||
if 'error' in response and response['error'] != None:
|
if 'error' in response and response['error'] is not None:
|
||||||
# If error is a dict get the message
|
# If error is a dict get the message
|
||||||
if isinstance(response['error'], dict):
|
if isinstance(response['error'], dict):
|
||||||
if 'message' in response['error']:
|
if 'message' in response['error']:
|
||||||
@@ -282,7 +280,7 @@ def auctions():
|
|||||||
|
|
||||||
# Sort
|
# Sort
|
||||||
sort = request.args.get("sort")
|
sort = request.args.get("sort")
|
||||||
if sort == None:
|
if sort is None:
|
||||||
sort = "time"
|
sort = "time"
|
||||||
sort = sort.lower()
|
sort = sort.lower()
|
||||||
sort_price = ""
|
sort_price = ""
|
||||||
@@ -296,7 +294,7 @@ def auctions():
|
|||||||
reverse = False
|
reverse = False
|
||||||
|
|
||||||
direction = request.args.get("direction")
|
direction = request.args.get("direction")
|
||||||
if direction == None:
|
if direction is None:
|
||||||
if sort == "time":
|
if sort == "time":
|
||||||
direction = "⬆"
|
direction = "⬆"
|
||||||
else:
|
else:
|
||||||
@@ -365,7 +363,7 @@ def revealAllBids():
|
|||||||
return redirect("/auctions?message=Failed to reveal bids")
|
return redirect("/auctions?message=Failed to reveal bids")
|
||||||
|
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
if response['error']['message'] == "Nothing to do.":
|
if response['error']['message'] == "Nothing to do.":
|
||||||
return redirect("/auctions?message=No reveals pending")
|
return redirect("/auctions?message=No reveals pending")
|
||||||
return redirect("/auctions?message=" + response['error']['message'])
|
return redirect("/auctions?message=" + response['error']['message'])
|
||||||
@@ -388,7 +386,7 @@ def redeemAllBids():
|
|||||||
return redirect("/auctions?message=Failed to redeem bids")
|
return redirect("/auctions?message=Failed to redeem bids")
|
||||||
|
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
if response['error']['message'] == "Nothing to do.":
|
if response['error']['message'] == "Nothing to do.":
|
||||||
return redirect("/auctions?message=No redeems pending")
|
return redirect("/auctions?message=No redeems pending")
|
||||||
return redirect("/auctions?message=" + response['error']['message'])
|
return redirect("/auctions?message=" + response['error']['message'])
|
||||||
@@ -410,7 +408,7 @@ def registerAllDomains():
|
|||||||
return redirect("/auctions?message=Failed to register domains")
|
return redirect("/auctions?message=Failed to register domains")
|
||||||
|
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
if response['error']['message'] == "Nothing to do.":
|
if response['error']['message'] == "Nothing to do.":
|
||||||
return redirect("/auctions?message=No domains to register")
|
return redirect("/auctions?message=No domains to register")
|
||||||
return redirect("/auctions?message=" + response['error']['message'])
|
return redirect("/auctions?message=" + response['error']['message'])
|
||||||
@@ -429,7 +427,7 @@ def finalizeAllBids():
|
|||||||
|
|
||||||
response = account_module.finalizeAll(request.cookies.get("account"))
|
response = account_module.finalizeAll(request.cookies.get("account"))
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
if response['error']['message'] == "Nothing to do.":
|
if response['error']['message'] == "Nothing to do.":
|
||||||
return redirect("/dashboard?message=No domains to finalize")
|
return redirect("/dashboard?message=No domains to finalize")
|
||||||
return redirect("/dashboard?message=" + response['error']['message'])
|
return redirect("/dashboard?message=" + response['error']['message'])
|
||||||
@@ -507,7 +505,6 @@ def search():
|
|||||||
domain_info = account_module.getDomain(search_term)
|
domain_info = account_module.getDomain(search_term)
|
||||||
owner = 'Unknown'
|
owner = 'Unknown'
|
||||||
dns = []
|
dns = []
|
||||||
txs = []
|
|
||||||
|
|
||||||
if domain_info:
|
if domain_info:
|
||||||
# Check if info and info.owner
|
# Check if info and info.owner
|
||||||
@@ -560,10 +557,10 @@ def manage(domain: str):
|
|||||||
dns = render.dns(dns)
|
dns = render.dns(dns)
|
||||||
|
|
||||||
errorMessage = request.args.get("error")
|
errorMessage = request.args.get("error")
|
||||||
if errorMessage == None:
|
if errorMessage is None:
|
||||||
errorMessage = ""
|
errorMessage = ""
|
||||||
address = request.args.get("address")
|
address = request.args.get("address")
|
||||||
if address == None:
|
if address is None:
|
||||||
address = ""
|
address = ""
|
||||||
|
|
||||||
finalize_time = ""
|
finalize_time = ""
|
||||||
@@ -607,7 +604,7 @@ def finalize(domain: str):
|
|||||||
|
|
||||||
domain = domain.lower()
|
domain = domain.lower()
|
||||||
response = account_module.finalize(request.cookies.get("account"),domain)
|
response = account_module.finalize(request.cookies.get("account"),domain)
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
print(response)
|
print(response)
|
||||||
return redirect("/manage/" + domain + "?error=" + response['error']['message'])
|
return redirect("/manage/" + domain + "?error=" + response['error']['message'])
|
||||||
|
|
||||||
@@ -626,7 +623,7 @@ def cancelTransfer(domain: str):
|
|||||||
domain = domain.lower()
|
domain = domain.lower()
|
||||||
response = account_module.cancelTransfer(request.cookies.get("account"),domain)
|
response = account_module.cancelTransfer(request.cookies.get("account"),domain)
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
print(response)
|
print(response)
|
||||||
return redirect("/manage/" + domain + "?error=" + response['error']['message'])
|
return redirect("/manage/" + domain + "?error=" + response['error']['message'])
|
||||||
|
|
||||||
@@ -645,9 +642,9 @@ def revokeInit(domain: str):
|
|||||||
domain = domain.lower()
|
domain = domain.lower()
|
||||||
|
|
||||||
content = f"Are you sure you want to revoke {domain}/?<br>"
|
content = f"Are you sure you want to revoke {domain}/?<br>"
|
||||||
content += f"This will return the domain to the auction pool and you will lose any funds spent on the domain.<br>"
|
content += "This will return the domain to the auction pool and you will lose any funds spent on the domain.<br>"
|
||||||
content += f"This cannot be undone after the transaction is sent.<br><br>"
|
content += "This cannot be undone after the transaction is sent.<br><br>"
|
||||||
content += f"Please enter your password to confirm."
|
content += "Please enter your password to confirm."
|
||||||
|
|
||||||
cancel = f"/manage/{domain}"
|
cancel = f"/manage/{domain}"
|
||||||
confirm = f"/manage/{domain}/revoke/confirm"
|
confirm = f"/manage/{domain}/revoke/confirm"
|
||||||
@@ -676,13 +673,13 @@ def revokeConfirm(domain: str):
|
|||||||
return redirect("/manage/" + domain + "?error=An error occurred. Please try again.")
|
return redirect("/manage/" + domain + "?error=An error occurred. Please try again.")
|
||||||
|
|
||||||
response = account_module.check_password(request.cookies.get("account"),password)
|
response = account_module.check_password(request.cookies.get("account"),password)
|
||||||
if response == False:
|
if not response:
|
||||||
return redirect("/manage/" + domain + "?error=Invalid password")
|
return redirect("/manage/" + domain + "?error=Invalid password")
|
||||||
|
|
||||||
|
|
||||||
response = account_module.revoke(request.cookies.get("account"),domain)
|
response = account_module.revoke(request.cookies.get("account"),domain)
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
print(response)
|
print(response)
|
||||||
return redirect("/manage/" + domain + "?error=" + response['error']['message'])
|
return redirect("/manage/" + domain + "?error=" + response['error']['message'])
|
||||||
|
|
||||||
@@ -722,7 +719,7 @@ def editPage(domain: str):
|
|||||||
|
|
||||||
|
|
||||||
user_edits = request.args.get("dns")
|
user_edits = request.args.get("dns")
|
||||||
if user_edits != None:
|
if user_edits is not None:
|
||||||
dns = urllib.parse.unquote(user_edits)
|
dns = urllib.parse.unquote(user_edits)
|
||||||
else:
|
else:
|
||||||
dns = account_module.getDNS(domain)
|
dns = account_module.getDNS(domain)
|
||||||
@@ -735,7 +732,7 @@ def editPage(domain: str):
|
|||||||
# Check if new records have been added
|
# Check if new records have been added
|
||||||
dnsType = request.args.get("type")
|
dnsType = request.args.get("type")
|
||||||
dnsValue = request.args.get("value")
|
dnsValue = request.args.get("value")
|
||||||
if dnsType != None and dnsValue != None:
|
if dnsType is not None and dnsValue is not None:
|
||||||
if dnsType != "DS":
|
if dnsType != "DS":
|
||||||
dns.append({"type": dnsType, "value": dnsValue})
|
dns.append({"type": dnsType, "value": dnsValue})
|
||||||
else:
|
else:
|
||||||
@@ -749,7 +746,7 @@ def editPage(domain: str):
|
|||||||
key_tag = int(ds[0])
|
key_tag = int(ds[0])
|
||||||
algorithm = int(ds[1])
|
algorithm = int(ds[1])
|
||||||
digest_type = int(ds[2])
|
digest_type = int(ds[2])
|
||||||
except:
|
except ValueError:
|
||||||
raw_dns = str(dns).replace("'",'"')
|
raw_dns = str(dns).replace("'",'"')
|
||||||
return redirect("/manage/" + domain + "/edit?dns=" + urllib.parse.quote(str(raw_dns)) + "&error=Invalid DS record")
|
return redirect("/manage/" + domain + "/edit?dns=" + urllib.parse.quote(str(raw_dns)) + "&error=Invalid DS record")
|
||||||
|
|
||||||
@@ -761,7 +758,7 @@ def editPage(domain: str):
|
|||||||
raw_dns = str(dns).replace("'",'"')
|
raw_dns = str(dns).replace("'",'"')
|
||||||
dns = render.dns(dns,True)
|
dns = render.dns(dns,True)
|
||||||
errorMessage = request.args.get("error")
|
errorMessage = request.args.get("error")
|
||||||
if errorMessage == None:
|
if errorMessage is None:
|
||||||
errorMessage = ""
|
errorMessage = ""
|
||||||
|
|
||||||
|
|
||||||
@@ -820,7 +817,7 @@ def transfer(domain):
|
|||||||
|
|
||||||
action = f"Send {domain}/ to {request.form.get('address')}"
|
action = f"Send {domain}/ to {request.form.get('address')}"
|
||||||
content = f"Are you sure you want to send {domain}/ to {toAddress}<br><br>"
|
content = f"Are you sure you want to send {domain}/ to {toAddress}<br><br>"
|
||||||
content += f"This requires sending a finalize transaction 2 days after the transfer is initiated."
|
content += "This requires sending a finalize transaction 2 days after the transfer is initiated."
|
||||||
|
|
||||||
cancel = f"/manage/{domain}?address={address}"
|
cancel = f"/manage/{domain}?address={address}"
|
||||||
confirm = f"/manage/{domain}/transfer/confirm?address={address}"
|
confirm = f"/manage/{domain}/transfer/confirm?address={address}"
|
||||||
@@ -847,9 +844,9 @@ def signMessage(domain):
|
|||||||
|
|
||||||
content = "Message to sign:<br><code>" + message + "</code><br><br>"
|
content = "Message to sign:<br><code>" + message + "</code><br><br>"
|
||||||
signedMessage = account_module.signMessage(request.cookies.get("account"),domain,message)
|
signedMessage = account_module.signMessage(request.cookies.get("account"),domain,message)
|
||||||
if signedMessage["error"] != None:
|
if signedMessage["error"] is not None:
|
||||||
return redirect("/manage/" + domain + "?error=" + signedMessage["error"])
|
return redirect("/manage/" + domain + "?error=" + signedMessage["error"])
|
||||||
content += f"Signature:<br><code>{signedMessage["result"]}</code><br><br>"
|
content += f"Signature:<br><code>{signedMessage['result']}</code><br><br>"
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
@@ -906,7 +903,7 @@ def auction(domain):
|
|||||||
|
|
||||||
domainInfo = account_module.getDomain(search_term)
|
domainInfo = account_module.getDomain(search_term)
|
||||||
error = request.args.get("error")
|
error = request.args.get("error")
|
||||||
if error == None:
|
if error is None:
|
||||||
error = ""
|
error = ""
|
||||||
|
|
||||||
if 'error' in domainInfo:
|
if 'error' in domainInfo:
|
||||||
@@ -916,9 +913,9 @@ def auction(domain):
|
|||||||
error=error)
|
error=error)
|
||||||
|
|
||||||
if domainInfo['info'] is None:
|
if domainInfo['info'] is None:
|
||||||
if 'registered' in domainInfo and domainInfo['registered'] == False and 'expired' in domainInfo and domainInfo['expired'] == False:
|
if 'registered' in domainInfo and not domainInfo['registered'] and 'expired' in domainInfo and not domainInfo['expired']:
|
||||||
# Needs to be registered
|
# Needs to be registered
|
||||||
next_action = f'ERROR GETTING NEXT STATE'
|
next_action = 'ERROR GETTING NEXT STATE'
|
||||||
else:
|
else:
|
||||||
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
next_action = f'<a href="/auction/{domain}/open">Open Auction</a>'
|
||||||
return render_template("auction.html", account=account,
|
return render_template("auction.html", account=account,
|
||||||
@@ -965,7 +962,7 @@ def auction(domain):
|
|||||||
elif stats['blocksUntilReveal'] == 2:
|
elif stats['blocksUntilReveal'] == 2:
|
||||||
next += "<br>LAST CHANCE TO BID"
|
next += "<br>LAST CHANCE TO BID"
|
||||||
elif stats['blocksUntilReveal'] == 3:
|
elif stats['blocksUntilReveal'] == 3:
|
||||||
next += f"<br>Next block is last chance to bid"
|
next += "<br>Next block is last chance to bid"
|
||||||
elif stats['blocksUntilReveal'] < 6:
|
elif stats['blocksUntilReveal'] < 6:
|
||||||
next += f"<br>Last chance to bid in {stats['blocksUntilReveal']-2} blocks"
|
next += f"<br>Last chance to bid in {stats['blocksUntilReveal']-2} blocks"
|
||||||
|
|
||||||
@@ -997,7 +994,7 @@ def rescan_auction(domain):
|
|||||||
|
|
||||||
domain = domain.lower()
|
domain = domain.lower()
|
||||||
|
|
||||||
response = account_module.rescan_auction(account,domain)
|
account_module.rescan_auction(account,domain)
|
||||||
return redirect("/auction/" + domain)
|
return redirect("/auction/" + domain)
|
||||||
|
|
||||||
@app.route('/auction/<domain>/bid')
|
@app.route('/auction/<domain>/bid')
|
||||||
@@ -1093,7 +1090,7 @@ def open_auction(domain):
|
|||||||
response = account_module.openAuction(request.cookies.get("account"),domain)
|
response = account_module.openAuction(request.cookies.get("account"),domain)
|
||||||
|
|
||||||
if 'error' in response:
|
if 'error' in response:
|
||||||
if response['error'] != None:
|
if response['error'] is not None:
|
||||||
return redirect("/auction/" + domain + "?error=" + response['error']['message'])
|
return redirect("/auction/" + domain + "?error=" + response['error']['message'])
|
||||||
|
|
||||||
return redirect(f"/success?tx={response['hash']}")
|
return redirect(f"/success?tx={response['hash']}")
|
||||||
@@ -1142,10 +1139,10 @@ def settings():
|
|||||||
return redirect("/logout")
|
return redirect("/logout")
|
||||||
|
|
||||||
error = request.args.get("error")
|
error = request.args.get("error")
|
||||||
if error == None:
|
if error is None:
|
||||||
error = ""
|
error = ""
|
||||||
success = request.args.get("success")
|
success = request.args.get("success")
|
||||||
if success == None:
|
if success is None:
|
||||||
success = ""
|
success = ""
|
||||||
|
|
||||||
|
|
||||||
@@ -1203,8 +1200,16 @@ def settings_action(action):
|
|||||||
|
|
||||||
|
|
||||||
if action == "zap":
|
if action == "zap":
|
||||||
resp = account_module.zapTXs(request.cookies.get("account"))
|
age = request.args.get("age", 1200)
|
||||||
if type(resp) == dict and 'error' in resp:
|
try:
|
||||||
|
age = int(age)
|
||||||
|
except ValueError:
|
||||||
|
age = 1200
|
||||||
|
if age < 0:
|
||||||
|
age = 1200
|
||||||
|
|
||||||
|
resp = account_module.zapTXs(request.cookies.get("account"),age)
|
||||||
|
if type(resp) is dict and 'error' in resp:
|
||||||
return redirect("/settings?error=" + str(resp['error']))
|
return redirect("/settings?error=" + str(resp['error']))
|
||||||
return redirect("/settings?success=Zapped transactions")
|
return redirect("/settings?success=Zapped transactions")
|
||||||
|
|
||||||
@@ -1225,11 +1230,23 @@ def settings_action(action):
|
|||||||
title="Restarting",
|
title="Restarting",
|
||||||
content="The node is restarting. This may take a minute or two. You can close this window.")
|
content="The node is restarting. This may take a minute or two. You can close this window.")
|
||||||
|
|
||||||
|
if action == "api-info":
|
||||||
|
content = f"API URL: <code>http://{account_module.HSD_IP}:{account_module.HSD_NODE_PORT}</code><br>"
|
||||||
|
content += f"Wallet URL: <code>http://{account_module.HSD_IP}:{account_module.HSD_WALLET_PORT}</code><br>"
|
||||||
|
content += f"API Key: <code>{account_module.HSD_API}</code><br><br>"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return render_template("message.html", account=account,
|
||||||
|
title="API Information",
|
||||||
|
content=content)
|
||||||
|
|
||||||
|
|
||||||
return redirect("/settings?error=Invalid action")
|
return redirect("/settings?error=Invalid action")
|
||||||
|
|
||||||
@app.route('/settings/upload', methods=['POST'])
|
@app.route('/settings/upload', methods=['POST'])
|
||||||
def upload_image():
|
def upload_image():
|
||||||
if not 'account' in request.cookies:
|
if 'account' not in request.cookies:
|
||||||
return redirect("/login?message=Not logged in")
|
return redirect("/login?message=Not logged in")
|
||||||
|
|
||||||
account = request.cookies.get("account")
|
account = request.cookies.get("account")
|
||||||
@@ -1253,7 +1270,7 @@ def upload_image():
|
|||||||
return redirect("/settings?error=An error occurred")
|
return redirect("/settings?error=An error occurred")
|
||||||
|
|
||||||
def latestVersion(branch):
|
def latestVersion(branch):
|
||||||
result = requests.get(f"https://git.woodburn.au/api/v1/repos/nathanwoodburn/firewalletbrowser/branches")
|
result = requests.get("https://git.woodburn.au/api/v1/repos/nathanwoodburn/firewalletbrowser/branches")
|
||||||
if result.status_code != 200:
|
if result.status_code != 200:
|
||||||
return "Error"
|
return "Error"
|
||||||
|
|
||||||
@@ -1290,7 +1307,7 @@ def login_post():
|
|||||||
account = request.form.get("account")
|
account = request.form.get("account")
|
||||||
password = request.form.get("password")
|
password = request.form.get("password")
|
||||||
|
|
||||||
if account == None or password == None:
|
if account is None or password is None:
|
||||||
wallets = account_module.listWallets()
|
wallets = account_module.listWallets()
|
||||||
wallets = render.wallets(wallets)
|
wallets = render.wallets(wallets)
|
||||||
return render_template("login.html",
|
return render_template("login.html",
|
||||||
@@ -1329,7 +1346,7 @@ def register():
|
|||||||
password = request.form.get("password")
|
password = request.form.get("password")
|
||||||
repeatPassword = request.form.get("password_repeat")
|
repeatPassword = request.form.get("password_repeat")
|
||||||
|
|
||||||
if account == None or password == None or repeatPassword == None:
|
if account is None or password is None or repeatPassword is None:
|
||||||
return render_template("register.html",
|
return render_template("register.html",
|
||||||
error="Invalid account or password",
|
error="Invalid account or password",
|
||||||
name=account,password=password,password_repeat=repeatPassword)
|
name=account,password=password,password_repeat=repeatPassword)
|
||||||
@@ -1376,7 +1393,7 @@ def import_wallet():
|
|||||||
repeatPassword = request.form.get("password_repeat")
|
repeatPassword = request.form.get("password_repeat")
|
||||||
seed = request.form.get("seed")
|
seed = request.form.get("seed")
|
||||||
|
|
||||||
if account == None or password == None or repeatPassword == None or seed == None:
|
if account is None or password is None or repeatPassword is None or seed is None:
|
||||||
return render_template("import-wallet.html",
|
return render_template("import-wallet.html",
|
||||||
error="Invalid account, password or seed",
|
error="Invalid account, password or seed",
|
||||||
name=account,password=password,password_repeat=repeatPassword,
|
name=account,password=password,password_repeat=repeatPassword,
|
||||||
@@ -1475,12 +1492,12 @@ def plugin(ptype,plugin):
|
|||||||
functions = plugins_module.getPluginFunctions(plugin)
|
functions = plugins_module.getPluginFunctions(plugin)
|
||||||
functions = render.plugin_functions(functions,plugin)
|
functions = render.plugin_functions(functions,plugin)
|
||||||
|
|
||||||
if data['verified'] == False:
|
if not data['verified']:
|
||||||
functions = "<div class='container-fluid'><div class='alert alert-warning' role='alert'>This plugin is not verified and is disabled for your protection. Please check the code before marking the plugin as verified <a href='/plugin/" + plugin + "/verify' class='btn btn-danger'>Verify</a></div></div>" + functions
|
functions = "<div class='container-fluid'><div class='alert alert-warning' role='alert'>This plugin is not verified and is disabled for your protection. Please check the code before marking the plugin as verified <a href='/plugin/" + plugin + "/verify' class='btn btn-danger'>Verify</a></div></div>" + functions
|
||||||
|
|
||||||
|
|
||||||
error = request.args.get("error")
|
error = request.args.get("error")
|
||||||
if error == None:
|
if error is None:
|
||||||
error = ""
|
error = ""
|
||||||
|
|
||||||
return render_template("plugin.html", account=account,
|
return render_template("plugin.html", account=account,
|
||||||
@@ -1506,7 +1523,7 @@ def plugin_verify(ptype,plugin):
|
|||||||
|
|
||||||
data = plugins_module.getPluginData(plugin)
|
data = plugins_module.getPluginData(plugin)
|
||||||
|
|
||||||
if data['verified'] == False:
|
if not data['verified']:
|
||||||
plugins_module.verifyPlugin(plugin)
|
plugins_module.verifyPlugin(plugin)
|
||||||
|
|
||||||
return redirect("/plugin/" + plugin)
|
return redirect("/plugin/" + plugin)
|
||||||
@@ -1593,7 +1610,7 @@ def api_hsd(function):
|
|||||||
if not domain:
|
if not domain:
|
||||||
return jsonify({"error": "No domain specified"}), 400
|
return jsonify({"error": "No domain specified"}), 400
|
||||||
domainInfo = account_module.getDomain(domain)
|
domainInfo = account_module.getDomain(domain)
|
||||||
if 'error' in domainInfo and domainInfo['error'] != None:
|
if 'error' in domainInfo and domainInfo['error'] is not None:
|
||||||
return jsonify({"error": domainInfo['error']}), 400
|
return jsonify({"error": domainInfo['error']}), 400
|
||||||
stats = domainInfo['info']['stats'] if 'stats' in domainInfo['info'] else {}
|
stats = domainInfo['info']['stats'] if 'stats' in domainInfo['info'] else {}
|
||||||
state = domainInfo['info']['state']
|
state = domainInfo['info']['state']
|
||||||
@@ -1628,7 +1645,7 @@ def api_hsd(function):
|
|||||||
elif stats['blocksUntilReveal'] == 2:
|
elif stats['blocksUntilReveal'] == 2:
|
||||||
next += "<br>LAST CHANCE TO BID"
|
next += "<br>LAST CHANCE TO BID"
|
||||||
elif stats['blocksUntilReveal'] == 3:
|
elif stats['blocksUntilReveal'] == 3:
|
||||||
next += f"<br>Next block is last chance to bid"
|
next += "<br>Next block is last chance to bid"
|
||||||
elif stats['blocksUntilReveal'] < 6:
|
elif stats['blocksUntilReveal'] < 6:
|
||||||
next += f"<br>Last chance to bid in {stats['blocksUntilReveal']-2} blocks"
|
next += f"<br>Last chance to bid in {stats['blocksUntilReveal']-2} blocks"
|
||||||
|
|
||||||
@@ -1714,7 +1731,7 @@ def api_wallet(function):
|
|||||||
|
|
||||||
if function == "domains":
|
if function == "domains":
|
||||||
domains = account_module.getDomains(account)
|
domains = account_module.getDomains(account)
|
||||||
if type(domains) == dict and 'error' in domains:
|
if type(domains) is dict and 'error' in domains:
|
||||||
return jsonify({"result": [], "error": domains['error']})
|
return jsonify({"result": [], "error": domains['error']})
|
||||||
|
|
||||||
# Add nameRender to each domain
|
# Add nameRender to each domain
|
||||||
@@ -1728,7 +1745,7 @@ def api_wallet(function):
|
|||||||
page = request.args.get('page', 1)
|
page = request.args.get('page', 1)
|
||||||
try:
|
try:
|
||||||
page = int(page)
|
page = int(page)
|
||||||
except:
|
except ValueError:
|
||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
if page < 1:
|
if page < 1:
|
||||||
@@ -1783,9 +1800,9 @@ def api_wallet(function):
|
|||||||
|
|
||||||
if function == "icon":
|
if function == "icon":
|
||||||
# Check if there is an icon
|
# Check if there is an icon
|
||||||
if not os.path.exists(f'user_data/images'):
|
if not os.path.exists('user_data/images'):
|
||||||
return send_file('templates/assets/img/HNS.png')
|
return send_file('templates/assets/img/HNS.png')
|
||||||
files = os.listdir(f'user_data/images')
|
files = os.listdir('user_data/images')
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.startswith(account):
|
if file.startswith(account):
|
||||||
return send_file(f'user_data/images/{file}')
|
return send_file(f'user_data/images/{file}')
|
||||||
@@ -1801,7 +1818,6 @@ def api_wallet_mobile(function):
|
|||||||
return jsonify({"error": "Not logged in"})
|
return jsonify({"error": "Not logged in"})
|
||||||
|
|
||||||
account = account_module.check_account(request.cookies.get("account"))
|
account = account_module.check_account(request.cookies.get("account"))
|
||||||
password = request.cookies.get("account","").split(":")[1]
|
|
||||||
if not account:
|
if not account:
|
||||||
return jsonify({"error": "Invalid account"})
|
return jsonify({"error": "Invalid account"})
|
||||||
|
|
||||||
@@ -1820,9 +1836,9 @@ def api_wallet_mobile(function):
|
|||||||
|
|
||||||
@app.route('/api/v1/icon/<account>')
|
@app.route('/api/v1/icon/<account>')
|
||||||
def api_icon(account):
|
def api_icon(account):
|
||||||
if not os.path.exists(f'user_data/images'):
|
if not os.path.exists('user_data/images'):
|
||||||
return send_file('templates/assets/img/HNS.png')
|
return send_file('templates/assets/img/HNS.png')
|
||||||
files = os.listdir(f'user_data/images')
|
files = os.listdir('user_data/images')
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.startswith(account):
|
if file.startswith(account):
|
||||||
return send_file(f'user_data/images/{file}')
|
return send_file(f'user_data/images/{file}')
|
||||||
@@ -1854,7 +1870,7 @@ def renderDomain(name: str) -> str:
|
|||||||
return f"{rendered}/ ({name})"
|
return f"{rendered}/ ({name})"
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
return f"{name}/"
|
return f"{name}/"
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -1906,3 +1922,9 @@ if __name__ == '__main__':
|
|||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
else:
|
else:
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
def tests():
|
||||||
|
assert blocks_to_time(6) == "1 hrs"
|
||||||
|
assert blocks_to_time(3) == "30 mins"
|
||||||
|
assert blocks_to_time(1) == "10 mins"
|
||||||
|
assert blocks_to_time(10) == "1 hrs 40 mins"
|
||||||
12
plugin.py
12
plugin.py
@@ -62,7 +62,8 @@ def listPlugins(update=False):
|
|||||||
try:
|
try:
|
||||||
with open("user_data/plugin_signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except Exception as e:
|
||||||
|
print(f"Error loading plugin signatures: {e}")
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("user_data/plugin_signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
@@ -87,7 +88,8 @@ def verifyPlugin(plugin: str):
|
|||||||
try:
|
try:
|
||||||
with open("user_data/plugin_signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except Exception as e:
|
||||||
|
print(f"Error loading plugin signatures: {e}")
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("user_data/plugin_signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
@@ -120,7 +122,8 @@ def getPluginData(pluginStr: str):
|
|||||||
try:
|
try:
|
||||||
with open("user_data/plugin_signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except Exception as e:
|
||||||
|
print(f"Error loading plugin signatures: {e}")
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("user_data/plugin_signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
@@ -171,7 +174,8 @@ def runPluginFunction(plugin: str, function: str, params: dict, authentication:
|
|||||||
try:
|
try:
|
||||||
with open("user_data/plugin_signatures.json", "r") as f:
|
with open("user_data/plugin_signatures.json", "r") as f:
|
||||||
signatures = json.load(f)
|
signatures = json.load(f)
|
||||||
except:
|
except Exception as e:
|
||||||
|
print(f"Error loading plugin signatures: {e}")
|
||||||
# Write a new signatures file
|
# Write a new signatures file
|
||||||
with open("user_data/plugin_signatures.json", "w") as f:
|
with open("user_data/plugin_signatures.json", "w") as f:
|
||||||
json.dump(signatures, f)
|
json.dump(signatures, f)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import json
|
|
||||||
import account
|
import account
|
||||||
import requests
|
import requests
|
||||||
import threading
|
import threading
|
||||||
@@ -127,7 +126,7 @@ def automations_background(authentication):
|
|||||||
account_name = account.check_account(authentication)
|
account_name = account.check_account(authentication)
|
||||||
password = ":".join(authentication.split(":")[1:])
|
password = ":".join(authentication.split(":")[1:])
|
||||||
|
|
||||||
if account_name == False:
|
if not account_name:
|
||||||
return {
|
return {
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Invalid account"
|
"message": "Invalid account"
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import json
|
|
||||||
import account
|
import account
|
||||||
import requests
|
import requests
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -384,7 +382,7 @@ def bid(params, authentication):
|
|||||||
bid = float(params["bid"])
|
bid = float(params["bid"])
|
||||||
blind = float(params["blind"])
|
blind = float(params["blind"])
|
||||||
blind+=bid
|
blind+=bid
|
||||||
except:
|
except ValueError:
|
||||||
return {
|
return {
|
||||||
"status":"Invalid bid amount",
|
"status":"Invalid bid amount",
|
||||||
"transaction":None
|
"transaction":None
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import json
|
import json
|
||||||
import account
|
|
||||||
import requests
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Plugin Data
|
# Plugin Data
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import json
|
|
||||||
import account
|
import account
|
||||||
import requests
|
import requests
|
||||||
import os
|
|
||||||
|
|
||||||
# Plugin Data
|
# Plugin Data
|
||||||
info = {
|
info = {
|
||||||
@@ -90,7 +88,7 @@ def main(params, authentication):
|
|||||||
return {"status": f"Failed: {batch['error']['message']}", "transaction": "None"}
|
return {"status": f"Failed: {batch['error']['message']}", "transaction": "None"}
|
||||||
|
|
||||||
if 'result' in batch:
|
if 'result' in batch:
|
||||||
if batch['result'] != None:
|
if batch['result'] is not None:
|
||||||
tx = batch['result']['hash']
|
tx = batch['result']['hash']
|
||||||
return {"status": "Success", "transaction": tx}
|
return {"status": "Success", "transaction": tx}
|
||||||
# Note only one batch can be sent at a time
|
# Note only one batch can be sent at a time
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ def status(params, authentication):
|
|||||||
response = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
response = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
if response.json()["success"] != True:
|
if not response.json()["success"]:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
return {"status": f"Connected to {instance}"}
|
return {"status": f"Connected to {instance}"}
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ def login(params, authentication):
|
|||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
|
|
||||||
if response.json()["success"] != True:
|
if not response.json()["success"]:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
|
|
||||||
auth = {
|
auth = {
|
||||||
@@ -146,7 +146,7 @@ def addDomain(params, authentication):
|
|||||||
zones = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
zones = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
||||||
if zones.status_code != 200:
|
if zones.status_code != 200:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
if zones.json()["success"] != True:
|
if not zones.json()["success"]:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
|
|
||||||
zones = zones.json()["data"]
|
zones = zones.json()["data"]
|
||||||
@@ -169,7 +169,7 @@ def addDomain(params, authentication):
|
|||||||
response = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
response = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
if response.json()["success"] != True:
|
if not response.json()["success"]:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
zoneID = response.json()["data"]["zone"]
|
zoneID = response.json()["data"]["zone"]
|
||||||
data = {
|
data = {
|
||||||
@@ -179,7 +179,7 @@ def addDomain(params, authentication):
|
|||||||
response = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
response = requests.post(f"https://{instance}/api", json=data, headers=headers)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
if response.json()["success"] != True:
|
if not response.json()["success"]:
|
||||||
return {"status": "Error connecting to Varo"}
|
return {"status": "Error connecting to Varo"}
|
||||||
zone = response.json()["data"]
|
zone = response.json()["data"]
|
||||||
|
|
||||||
|
|||||||
52
render.py
52
render.py
@@ -2,9 +2,7 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from domainLookup import punycode_to_emoji
|
|
||||||
import os
|
import os
|
||||||
from handywrapper import api
|
|
||||||
import threading
|
import threading
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -55,7 +53,7 @@ def domains(domains, mobile=False):
|
|||||||
|
|
||||||
link = f'/manage/{domain["name"]}'
|
link = f'/manage/{domain["name"]}'
|
||||||
link_action = "Manage"
|
link_action = "Manage"
|
||||||
if domain['registered'] == False:
|
if not domain['registered']:
|
||||||
link_action = "Register"
|
link_action = "Register"
|
||||||
link = f'/auction/{domain["name"]}/register'
|
link = f'/auction/{domain["name"]}/register'
|
||||||
|
|
||||||
@@ -182,7 +180,7 @@ def transactions(txs):
|
|||||||
elif amount > 0:
|
elif amount > 0:
|
||||||
amount = f"<span style='color: green;'>+{amount:,.2f}</span>"
|
amount = f"<span style='color: green;'>+{amount:,.2f}</span>"
|
||||||
else:
|
else:
|
||||||
amount = f"<span style='color: gray;'>0.00</span>"
|
amount = "<span style='color: gray;'>0.00</span>"
|
||||||
|
|
||||||
|
|
||||||
# hash = f"<a target='_blank' href='{TX_EXPLORER_URL}{hash}'>{hash[:8]}...</a>"
|
# hash = f"<a target='_blank' href='{TX_EXPLORER_URL}{hash}'>{hash[:8]}...</a>"
|
||||||
@@ -253,7 +251,7 @@ def txs(data):
|
|||||||
amount = entry['amount']
|
amount = entry['amount']
|
||||||
amount = amount / 1000000
|
amount = amount / 1000000
|
||||||
|
|
||||||
if entry['blind'] == None:
|
if entry['blind'] is None:
|
||||||
html_output += f"<td>{amount:,.2f} HNS</td>\n"
|
html_output += f"<td>{amount:,.2f} HNS</td>\n"
|
||||||
else:
|
else:
|
||||||
blind = entry['blind']
|
blind = entry['blind']
|
||||||
@@ -261,7 +259,7 @@ def txs(data):
|
|||||||
html_output += f"<td>{amount:,.2f} + {blind:,.2f} HNS</td>\n"
|
html_output += f"<td>{amount:,.2f} + {blind:,.2f} HNS</td>\n"
|
||||||
|
|
||||||
html_output += f"<td>{timestamp_to_readable_time(entry['time'])}</td>\n"
|
html_output += f"<td>{timestamp_to_readable_time(entry['time'])}</td>\n"
|
||||||
html_output += f"</tr>\n"
|
html_output += "</tr>\n"
|
||||||
|
|
||||||
return html_output
|
return html_output
|
||||||
|
|
||||||
@@ -316,13 +314,13 @@ def bids(bids,reveals):
|
|||||||
html += f"<td>{value:,.2f} HNS</td>"
|
html += f"<td>{value:,.2f} HNS</td>"
|
||||||
html += f"<td>{bidValue:,.2f} HNS</td>"
|
html += f"<td>{bidValue:,.2f} HNS</td>"
|
||||||
else:
|
else:
|
||||||
html += f"<td>Hidden until reveal</td>"
|
html += "<td>Hidden until reveal</td>"
|
||||||
html += f"<td>Hidden until reveal</td>"
|
html += "<td>Hidden until reveal</td>"
|
||||||
|
|
||||||
if bid['own']:
|
if bid['own']:
|
||||||
html += "<td>You</td>"
|
html += "<td>You</td>"
|
||||||
else:
|
else:
|
||||||
html += f"<td>Unknown</td>"
|
html += "<td>Unknown</td>"
|
||||||
|
|
||||||
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' target='_blank' href='{TX_EXPLORER_URL}{bid['prevout']['hash']}'>Bid TX 🔗</a></td>"
|
html += f"<td><a class='text-decoration-none' style='color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));' target='_blank' href='{TX_EXPLORER_URL}{bid['prevout']['hash']}'>Bid TX 🔗</a></td>"
|
||||||
html += "</tr>"
|
html += "</tr>"
|
||||||
@@ -410,22 +408,22 @@ def plugin_functions(functions, pluginName):
|
|||||||
functionType = functions[function]["type"]
|
functionType = functions[function]["type"]
|
||||||
|
|
||||||
|
|
||||||
html += f'<div class="card" style="margin-top: 50px;">'
|
html += '<div class="card" style="margin-top: 50px;">'
|
||||||
html += f'<div class="card-body">'
|
html += '<div class="card-body">'
|
||||||
html += f'<h4 class="card-title">{name}</h4>'
|
html += f'<h4 class="card-title">{name}</h4>'
|
||||||
html += f'<h6 class="text-muted card-subtitle mb-2">{description}</h6>'
|
html += f'<h6 class="text-muted card-subtitle mb-2">{description}</h6>'
|
||||||
html += f'<h6 class="text-muted card-subtitle mb-2">Function type: {functionType.capitalize()}</h6>'
|
html += f'<h6 class="text-muted card-subtitle mb-2">Function type: {functionType.capitalize()}</h6>'
|
||||||
|
|
||||||
if functionType != "default":
|
if functionType != "default":
|
||||||
html += f'<p class="card-text">Returns: {returns}</p>'
|
html += f'<p class="card-text">Returns: {returns}</p>'
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Form
|
# Form
|
||||||
html += f'<form method="post" style="padding: 20px;" action="/plugin/{pluginName}/{function}">'
|
html += f'<form method="post" style="padding: 20px;" action="/plugin/{pluginName}/{function}">'
|
||||||
for param in params:
|
for param in params:
|
||||||
html += f'<div style="margin-bottom: 20px;">'
|
html += '<div style="margin-bottom: 20px;">'
|
||||||
paramName = params[param]["name"]
|
paramName = params[param]["name"]
|
||||||
paramType = params[param]["type"]
|
paramType = params[param]["type"]
|
||||||
if paramType == "text":
|
if paramType == "text":
|
||||||
@@ -449,14 +447,14 @@ def plugin_functions(functions, pluginName):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
|
|
||||||
html += f'<button type="submit" class="btn btn-primary">Submit</button>'
|
html += '<button type="submit" class="btn btn-primary">Submit</button>'
|
||||||
html += f'</form>'
|
html += '</form>'
|
||||||
# For debugging
|
# For debugging
|
||||||
html += f'<p class="card-text">Returns: {returns}</p>'
|
html += f'<p class="card-text">Returns: {returns}</p>'
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
|
|
||||||
|
|
||||||
return html
|
return html
|
||||||
@@ -468,16 +466,16 @@ def plugin_output(outputs, returns):
|
|||||||
for returnOutput in returns:
|
for returnOutput in returns:
|
||||||
if returnOutput not in outputs:
|
if returnOutput not in outputs:
|
||||||
continue
|
continue
|
||||||
html += f'<div class="card" style="margin-top: 50px; margin-bottom: 50px;">'
|
html += '<div class="card" style="margin-top: 50px; margin-bottom: 50px;">'
|
||||||
html += f'<div class="card-body">'
|
html += '<div class="card-body">'
|
||||||
html += f'<h4 class="card-title">{returns[returnOutput]["name"]}</h4>'
|
html += f'<h4 class="card-title">{returns[returnOutput]["name"]}</h4>'
|
||||||
|
|
||||||
output = outputs[returnOutput]
|
output = outputs[returnOutput]
|
||||||
if returns[returnOutput]["type"] == "list":
|
if returns[returnOutput]["type"] == "list":
|
||||||
html += f'<ul>'
|
html += '<ul>'
|
||||||
for item in output:
|
for item in output:
|
||||||
html += f'<li>{item}</li>'
|
html += f'<li>{item}</li>'
|
||||||
html += f'</ul>'
|
html += '</ul>'
|
||||||
elif returns[returnOutput]["type"] == "text":
|
elif returns[returnOutput]["type"] == "text":
|
||||||
html += f'<p>{output}</p>'
|
html += f'<p>{output}</p>'
|
||||||
elif returns[returnOutput]["type"] == "tx":
|
elif returns[returnOutput]["type"] == "tx":
|
||||||
@@ -487,8 +485,8 @@ def plugin_output(outputs, returns):
|
|||||||
html += render_template('components/dns-output.html', dns=dns(output))
|
html += render_template('components/dns-output.html', dns=dns(output))
|
||||||
|
|
||||||
|
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
html += f'</div>'
|
html += '</div>'
|
||||||
return html
|
return html
|
||||||
|
|
||||||
def plugin_output_dash(outputs, returns):
|
def plugin_output_dash(outputs, returns):
|
||||||
@@ -498,7 +496,7 @@ def plugin_output_dash(outputs, returns):
|
|||||||
for returnOutput in returns:
|
for returnOutput in returns:
|
||||||
if returnOutput not in outputs:
|
if returnOutput not in outputs:
|
||||||
continue
|
continue
|
||||||
if outputs[returnOutput] == None:
|
if outputs[returnOutput] is None:
|
||||||
continue
|
continue
|
||||||
html += render_template('components/dashboard-plugin.html', name=returns[returnOutput]["name"], output=outputs[returnOutput])
|
html += render_template('components/dashboard-plugin.html', name=returns[returnOutput]["name"], output=outputs[returnOutput])
|
||||||
return html
|
return html
|
||||||
@@ -517,7 +515,7 @@ def renderDomain(name: str) -> str:
|
|||||||
return f"{rendered}/ ({name})"
|
return f"{rendered}/ ({name})"
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
return f"{name}/"
|
return f"{name}/"
|
||||||
|
|
||||||
def renderDomainAsync(namehash: str) -> None:
|
def renderDomainAsync(namehash: str) -> None:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
from main import app
|
from main import app
|
||||||
@@ -39,6 +38,6 @@ if __name__ == '__main__':
|
|||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
print(f'Starting server with Waitress on {platform.system()} with {threads} threads...', flush=True)
|
print(f'Starting server with Waitress on {platform.system()} with {threads} threads...', flush=True)
|
||||||
print(f'Press Ctrl+C to stop the server', flush=True)
|
print('Press Ctrl+C to stop the server', flush=True)
|
||||||
print(f'Serving on http://0.0.0.0:5000/', flush=True)
|
print('Serving on http://0.0.0.0:5000/', flush=True)
|
||||||
serve(app, host="0.0.0.0", port=5000, threads=threads)
|
serve(app, host="0.0.0.0", port=5000, threads=threads)
|
||||||
|
|||||||
@@ -85,6 +85,12 @@
|
|||||||
<h3>Delete unconfirmed transactions</h3><span>This will only remove pending tx from the wallet older than 20 minutes (~ 2 blocks)</span>
|
<h3>Delete unconfirmed transactions</h3><span>This will only remove pending tx from the wallet older than 20 minutes (~ 2 blocks)</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<div><a class="btn btn-primary stick-right" role="button" href="/settings/api-info">API Info</a>
|
||||||
|
<h3>View API Information</h3><span>View information about the connected HSD node's API</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
{% if internal %}
|
{% if internal %}
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div><a class="btn btn-primary stick-right" role="button" href="/settings/restart">Restart Node</a>
|
<div><a class="btn btn-primary stick-right" role="button" href="/settings/restart">Restart Node</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user