Compare commits
14 Commits
feat/ci-te
...
feat/notic
| Author | SHA1 | Date | |
|---|---|---|---|
|
59000afa87
|
|||
|
699a74f093
|
|||
|
6096f82c4d
|
|||
|
4353eb8fa4
|
|||
|
344cde07d0
|
|||
|
2fb841aeaf
|
|||
|
60df317f78
|
|||
|
4c1ea9fb12
|
|||
|
58ed636ce3
|
|||
|
e537c323c2
|
|||
|
812fc84d3e
|
|||
|
6d318a597b
|
|||
|
83bd6b9643
|
|||
|
c93b2652f5
|
@@ -1,10 +1,10 @@
|
||||
name: Test Python Compatibility
|
||||
run-name: Test Python Compatibility
|
||||
name: Tests and Linting
|
||||
run-name: Python Compatibility and Linting tests
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
Python-Compatibility:
|
||||
Tests-Linting:
|
||||
runs-on: [ubuntu-latest, amd]
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
strategy:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
fi
|
||||
pip install pytest ruff
|
||||
|
||||
- name: Run tests
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
echo "Testing with Python ${{ matrix.python-version }}"
|
||||
python -m pytest main.py
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,6 +17,6 @@ cache/
|
||||
build/
|
||||
dist/
|
||||
hsd/
|
||||
hsd-data/
|
||||
hsd_data/
|
||||
hsd.lock
|
||||
hsdconfig.json
|
||||
|
||||
@@ -50,7 +50,7 @@ LABEL org.opencontainers.image.title="FireWallet (HSD)" \
|
||||
|
||||
|
||||
|
||||
VOLUME ["/app/hsd-data", "/app/user_data"]
|
||||
VOLUME ["/app/hsd_data", "/app/user_data"]
|
||||
|
||||
|
||||
ENTRYPOINT ["python3"]
|
||||
|
||||
Binary file not shown.
13
README.md
13
README.md
@@ -13,6 +13,17 @@ cp example.env .env
|
||||
Edit .env to have your HSD api key.
|
||||
If you have HSD runnning on a separate computer also add the IP here
|
||||
|
||||
For a quick and easy installation on ubuntu/debian you can run the install.sh script
|
||||
```bash
|
||||
curl https://firewallet.au/install.sh | bash
|
||||
```
|
||||
This will install all dependencies (including Node/NPM for an internal HSD node), create a python virtual environment and install the required python packages.
|
||||
After the script has run you can start the wallet with
|
||||
```bash
|
||||
./start.sh
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Make sure HSD is running then run the following commands:
|
||||
@@ -157,7 +168,7 @@ walletMigrate: <int> (for users migrating from older versions of HSD)
|
||||
|
||||
If you find FireWallet useful and would like to support its continued development, please consider making a donation. Your contributions help maintain the project and develop new features.
|
||||
|
||||
HNS donations can be sent to: `hs1qh7uzytf2ftwkd9dmjjs7az9qfver5m7dd7x4ej`
|
||||
HNS donations can be sent to: `hs1qh7uzytf2ftwkd9dmjjs7az9qfver5m7dd7x4ej`
|
||||
Other donation options can be found at [my website](https://nathan.woodburn.au/donate)
|
||||
|
||||
Thank you for your support!
|
||||
|
||||
@@ -1582,9 +1582,7 @@ def resendTXs():
|
||||
}
|
||||
|
||||
|
||||
def zapTXs(account):
|
||||
age = 60 * 20 # 20 minutes
|
||||
|
||||
def zapTXs(account, age=1200):
|
||||
account_name = check_account(account)
|
||||
|
||||
if not account_name:
|
||||
@@ -1806,7 +1804,7 @@ def checkPreRequisites() -> dict[str, bool]:
|
||||
|
||||
try:
|
||||
# Check if git is installed
|
||||
gitSubprocess = subprocess.run(["git", "-v"], capture_output=True, text=True,timeout=2)
|
||||
gitSubprocess = subprocess.run(["git", "--version"], capture_output=True, text=True,timeout=2)
|
||||
if gitSubprocess.returncode == 0:
|
||||
prerequisites["git"] = True
|
||||
except Exception:
|
||||
@@ -1902,7 +1900,7 @@ def hsdStart():
|
||||
chain_migrate = HSD_CONFIG.get("chainMigrate", False)
|
||||
wallet_migrate = HSD_CONFIG.get("walletMigrate", False)
|
||||
spv = HSD_CONFIG.get("spv", False)
|
||||
prefix = HSD_CONFIG.get("prefix", os.path.join(os.getcwd(), "hsd-data"))
|
||||
prefix = HSD_CONFIG.get("prefix", os.path.join(os.getcwd(), "hsd_data"))
|
||||
|
||||
|
||||
# Base command
|
||||
@@ -1978,7 +1976,6 @@ def hsdRestart():
|
||||
time.sleep(2)
|
||||
hsdStart()
|
||||
|
||||
|
||||
hsdInit()
|
||||
hsdStart()
|
||||
# endregion
|
||||
@@ -4,10 +4,8 @@ services:
|
||||
ports:
|
||||
- "5000:5000"
|
||||
volumes:
|
||||
- hsd_data:/app/hsd-data
|
||||
- hsd_data:/app/hsd_data
|
||||
- user_data:/app/user_data
|
||||
environment:
|
||||
- INTERNAL_HSD=true
|
||||
|
||||
volumes:
|
||||
hsd_data:
|
||||
|
||||
81
install.sh
Executable file
81
install.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
install_command=""
|
||||
|
||||
# Check if currently in the FireWalletBrowser directory
|
||||
if [ -f "server.py" ]; then
|
||||
echo "Please run this script from outside the FireWalletBrowser directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Starting installation of FireWalletBrowser..."
|
||||
|
||||
# Check if OS is using apt package manager (Debian/Ubuntu)
|
||||
if command -v apt-get &> /dev/null; then
|
||||
install_command="sudo apt-get install -y"
|
||||
dependencies=(git curl wget python3 python3-pip python3-venv)
|
||||
echo "Detected apt package manager."
|
||||
# Check if OS is using pacman package manager (Arch Linux)
|
||||
elif command -v pacman &> /dev/null; then
|
||||
install_command="sudo pacman -S"
|
||||
dependencies=(git curl wget python3 python-pip)
|
||||
echo "Detected pacman package manager."
|
||||
else
|
||||
echo "Unsupported package manager. Please install dependencies manually."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# List of dependencies to install
|
||||
# Install dependencies
|
||||
for package in "${dependencies[@]}"; do
|
||||
# Check if the package is already installed
|
||||
if command -v $package &> /dev/null || dpkg -s $package &> /dev/null || pacman -Qi $package &> /dev/null; then
|
||||
echo "$package is already installed."
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
echo "Installing $package..."
|
||||
$install_command $package
|
||||
# Check if the installation was successful
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to install $package. Please check your package manager settings."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! command -v node &> /dev/null || ! command -v npm &> /dev/null; then
|
||||
echo "Installing Node.js and npm..."
|
||||
# Download and install nvm:
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
||||
# in lieu of restarting the shell
|
||||
\. "$HOME/.nvm/nvm.sh"
|
||||
nvm install 20
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to install Node.js and npm. Please install them manually."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Node.js and npm are already installed."
|
||||
fi
|
||||
|
||||
# Clone repo
|
||||
git clone https://git.woodburn.au/nathanwoodburn/firewalletbrowser.git
|
||||
|
||||
# Setup venv
|
||||
cd firewalletbrowser || exit 1
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
# Install python dependencies
|
||||
python3 -m pip install -r requirements.txt
|
||||
|
||||
# Write .env file
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "Creating .env file..."
|
||||
echo "INTERNAL_HSD=true" > .env
|
||||
echo "Created .env file with INTERNAL Node enabled."
|
||||
fi
|
||||
|
||||
echo "Installation complete. You can start the application by running ./start.sh"
|
||||
138
main.py
138
main.py
@@ -1,6 +1,7 @@
|
||||
import io
|
||||
import json
|
||||
import random
|
||||
import sqlite3
|
||||
import sys
|
||||
from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory,send_file
|
||||
import os
|
||||
@@ -82,10 +83,17 @@ def index():
|
||||
commit = info['commit']
|
||||
if commit != latestVersion(branch):
|
||||
print("New version available",flush=True)
|
||||
plugins += render_template('components/dashboard-alert.html', name='Update', output='A new version of FireWallet is available')
|
||||
plugins += render_template('components/dashboard-alert.html', name='Update', output='A new version of FireWallet is available')
|
||||
|
||||
alerts = get_alerts(account)
|
||||
for alert in alerts:
|
||||
output_html = alert['output']
|
||||
# Add a dismiss button
|
||||
output_html += f" <a href='/dismiss/{alert['id']}' class='btn btn-secondary btn-sm' style='margin:none;'>Dismiss</a>"
|
||||
plugins += render_template('components/dashboard-alert.html', name=alert['name'], output=output_html)
|
||||
|
||||
return render_template("index.html", account=account, plugins=plugins)
|
||||
|
||||
|
||||
def reverseDirection(direction: str):
|
||||
if direction == "⬆":
|
||||
return "⬇"
|
||||
@@ -1200,7 +1208,15 @@ def settings_action(action):
|
||||
|
||||
|
||||
if action == "zap":
|
||||
resp = account_module.zapTXs(request.cookies.get("account"))
|
||||
age = request.args.get("age", 1200)
|
||||
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?success=Zapped transactions")
|
||||
@@ -1221,6 +1237,18 @@ def settings_action(action):
|
||||
return render_template("message.html", account=account,
|
||||
title="Restarting",
|
||||
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")
|
||||
|
||||
@@ -1410,7 +1438,7 @@ def import_wallet():
|
||||
name=account,password=password,password_repeat=repeatPassword,
|
||||
seed=seed)
|
||||
|
||||
|
||||
add_alert("Rescan needed", "Please rescan the wallet after importing to see all transactions", account)
|
||||
# Set the cookie
|
||||
response = make_response(redirect("/"))
|
||||
response.set_cookie("account", account+":"+password)
|
||||
@@ -1853,6 +1881,108 @@ def renderDomain(name: str) -> str:
|
||||
except Exception:
|
||||
return f"{name}/"
|
||||
|
||||
def get_alerts(account:str) -> list:
|
||||
"""
|
||||
Get alerts to show on the dashboard.
|
||||
"""
|
||||
|
||||
alerts = []
|
||||
|
||||
# Check if the node is connected
|
||||
if not account_module.hsdConnected():
|
||||
alerts.append({
|
||||
"name": "Node",
|
||||
"output": "HSD node is not connected. Please check your settings."
|
||||
})
|
||||
return alerts
|
||||
|
||||
# Check if the wallet is synced
|
||||
wallet_status = account_module.getWalletStatus()
|
||||
if wallet_status != "Ready":
|
||||
alerts.append({
|
||||
"name": "Wallet",
|
||||
"output": f"The wallet is not synced ({wallet_status}). Please wait for it to sync."
|
||||
})
|
||||
print(account)
|
||||
# Try to read from notifications sqlite database
|
||||
if os.path.exists("user_data/notifications.db"):
|
||||
try:
|
||||
conn = sqlite3.connect("user_data/notifications.db")
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT id, name, message FROM notifications WHERE read=0 AND (account=? OR account='all')", (account,))
|
||||
rows = c.fetchall()
|
||||
for row in rows:
|
||||
alerts.append({
|
||||
"id": row[0],
|
||||
"name": row[1],
|
||||
"output": row[2]
|
||||
})
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"Error reading notifications: {e}",flush=True)
|
||||
pass
|
||||
|
||||
return alerts
|
||||
|
||||
def add_alert(name:str,output:str,account:str="all"):
|
||||
"""
|
||||
Add an alert to the notifications database.
|
||||
|
||||
name: Name of the alert
|
||||
output: Message of the alert
|
||||
account: Account to add the alert for (default: all)
|
||||
"""
|
||||
if not os.path.exists("user_data/notifications.db"):
|
||||
conn = sqlite3.connect("user_data/notifications.db")
|
||||
c = conn.cursor()
|
||||
c.execute("CREATE TABLE notifications (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, message TEXT, account TEXT, read INTEGER DEFAULT 0)")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect("user_data/notifications.db")
|
||||
c = conn.cursor()
|
||||
c.execute("INSERT INTO notifications (name, message, account) VALUES (?, ?, ?)", (name, output, account))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"Error adding notification: {e}",flush=True)
|
||||
pass
|
||||
|
||||
def dismiss_alert(alert_id:int,account:str="all"):
|
||||
"""
|
||||
Mark an alert as read.
|
||||
|
||||
alert_id: ID of the alert to dismiss
|
||||
account: Account to dismiss the alert for (default: all)
|
||||
"""
|
||||
if not os.path.exists("user_data/notifications.db"):
|
||||
return
|
||||
|
||||
try:
|
||||
conn = sqlite3.connect("user_data/notifications.db")
|
||||
c = conn.cursor()
|
||||
c.execute("UPDATE notifications SET read=1 WHERE id=?", (alert_id,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"Error dismissing notification: {e}",flush=True)
|
||||
pass
|
||||
|
||||
@app.route('/dismiss/<int:alert_id>')
|
||||
@app.route('/api/v1/dismiss/<int:alert_id>')
|
||||
def dismiss_alert_route(alert_id):
|
||||
# Check if the user is logged in
|
||||
if request.cookies.get("account") is None:
|
||||
return redirect("/login")
|
||||
|
||||
account = account_module.check_account(request.cookies.get("account"))
|
||||
if not account:
|
||||
return redirect("/logout")
|
||||
|
||||
dismiss_alert(alert_id,account)
|
||||
return redirect(request.referrer or "/")
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
9
start.sh
Executable file
9
start.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Find if .venv exists
|
||||
if [ -d ".venv" ]; then
|
||||
echo "Virtual environment found. Activating..."
|
||||
source .venv/bin/activate
|
||||
fi
|
||||
|
||||
python3 server.py
|
||||
@@ -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>
|
||||
</div>
|
||||
</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 %}
|
||||
<li class="list-group-item">
|
||||
<div><a class="btn btn-primary stick-right" role="button" href="/settings/restart">Restart Node</a>
|
||||
|
||||
Reference in New Issue
Block a user