mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Create Specter Desktop tray app (#273)
* Add basic tray app * Run specterd from release * Start on launch * Support remote node * Auto whitelist remote node url * Windows related fixes * fixes * pyinstaller on windows * update instructions * update pyinstaller readme Co-authored-by: Stepan Snigirev <snigirev.stepan@gmail.com>
This commit is contained in:
parent
5699a5fb06
commit
2a799dde13
12 changed files with 669 additions and 12 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -10,3 +10,5 @@ dist
|
|||
.vscode
|
||||
cert.pem
|
||||
key.pem
|
||||
*.dmg
|
||||
pyinstaller/specterd/*
|
||||
|
|
|
|||
|
|
@ -1,11 +1,74 @@
|
|||
# Pyinstaller build
|
||||
|
||||
Install `pyinstaller`:
|
||||
`cd` into this directory (`specter-desktop/pyinstaller`) and install requirements:
|
||||
|
||||
```bash
|
||||
$ pip3 install pyinstaller
|
||||
$ pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
`cd` into this directory (`specter-desktop/pyinstaller`) and run:
|
||||
Now run:
|
||||
|
||||
```bash
|
||||
$ pyinstaller --onefile specterd.spec
|
||||
$ pyinstaller specterd.spec
|
||||
```
|
||||
|
||||
And for HWIBridge, run:
|
||||
|
||||
```bash
|
||||
pyinstaller hwibridge.spec
|
||||
```
|
||||
|
||||
# Building Specter launcher (tray app)
|
||||
|
||||
## Creating a Windows setup file
|
||||
|
||||
From Powershell:
|
||||
|
||||
1. Build `specterd` and `hwibridge` in onedir mode:
|
||||
|
||||
```bash
|
||||
pyinstaller specterd_onedir.spec
|
||||
pyinstaller hwibridge_onedir.spec
|
||||
```
|
||||
|
||||
You should get two folders in the `dist` folder: `specterd` and `hwibridge`.
|
||||
|
||||
2. Copy `specterd` folder from `dist` folder to this directory.
|
||||
3. Copy `hwibridge.exe` and `hwibridge.exe.manifest` from `dist\hwibridge\` to `specterd` folder.
|
||||
4. Run `pyinstaller specter_desktop.spec` - this should create a `specter_desktop` folder in the `dist` directory. Check that it works by running `dist\specter_desktop\specter_desktop.exe`
|
||||
5. Create an installer using [InnoSetup](https://jrsoftware.org/isdl.php#stable), select `dist\specter_desktop\specter_desktop.exe` as main executable, add `dist\specter_desktop` folder to the setup wizard as well.
|
||||
|
||||
## Creating a DMG for macOS
|
||||
|
||||
1. Build `specterd` and `hwibridge` in onedir mode:
|
||||
|
||||
```bash
|
||||
pyinstaller specterd_onedir.spec
|
||||
pyinstaller hwibridge_onedir.spec
|
||||
```
|
||||
|
||||
You should get two folders in the `dist` folder: `specterd` and `hwibridge`.
|
||||
|
||||
2. Copy `specterd` folder from `dist` folder to this directory: `cp -r dist/specterd/ ./specterd`
|
||||
3. Copy `hwibridge` binary from `dist/hwibridge` to `specterd` folder: `cp dist/hwibridge/hwibridge specterd`.
|
||||
4. Now in the terminal, run `pyinstaller specter_desktop.spec` (you might need to use sudo). This should create a new Specter and Specter.app files.
|
||||
5. The `Specter.app` file is the executable macOS app we will need to package now as a `.dmg` for distribution.
|
||||
6. Make sure you have [`NPM`](https://www.npmjs.com/get-npm) installed, and run `npm install --global create-dmg`.
|
||||
7. Now run `create-dmg 'dist/Specter.app'`. This should generate a new `Specter 0.0.0.dmg`.
|
||||
8. The `.dmg` should now be ready to use! Note: You can rename the `.dmg` file to have the proper version (or just say `Specter`).
|
||||
|
||||
## Creating a binary for Linux
|
||||
|
||||
1. Build `specterd` and `hwibridge` in onedir mode:
|
||||
|
||||
```bash
|
||||
pyinstaller specterd_onedir.spec
|
||||
pyinstaller hwibridge_onedir.spec
|
||||
```
|
||||
|
||||
You should get two folders in the `dist` folder: `specterd` and `hwibridge`.
|
||||
|
||||
2. Copy `specterd` folder from `dist` folder to this directory: `cp -r dist/specterd/ ./specterd`
|
||||
3. Copy `hwibridge` binary from `dist/hwibridge` to `specterd` folder: `cp dist/hwibridge/hwibridge specterd`.
|
||||
4. Run `pyinstaller specter_desktop.spec`. This should create a Specter executable.
|
||||
|
||||
|
|
|
|||
65
pyinstaller/hwibridge_onedir.spec
Normal file
65
pyinstaller/hwibridge_onedir.spec
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
import platform
|
||||
import subprocess
|
||||
import mnemonic, os
|
||||
|
||||
mnemonic_path = os.path.join(mnemonic.__path__[0], "wordlist")
|
||||
|
||||
block_cipher = None
|
||||
|
||||
binaries = []
|
||||
if platform.system() == 'Windows':
|
||||
binaries = [("./windll/libusb-1.0.dll", ".")]
|
||||
elif platform.system() == 'Linux':
|
||||
if platform.processor() == 'aarch64': #ARM 64 bit
|
||||
binaries = [("/lib/aarch64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
else:
|
||||
binaries = [("/lib/x86_64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
elif platform.system() == 'Darwin':
|
||||
find_brew_libusb_proc = subprocess.Popen(['brew', '--prefix', 'libusb'], stdout=subprocess.PIPE)
|
||||
libusb_path = find_brew_libusb_proc.communicate()[0]
|
||||
binaries = [(libusb_path.rstrip().decode() + "/lib/libusb-1.0.dylib", ".")]
|
||||
|
||||
a = Analysis(['hwibridge.py'],
|
||||
binaries=binaries,
|
||||
datas=[('../src/cryptoadvance/specter/templates', 'templates'),
|
||||
('../src/cryptoadvance/specter/static', 'static'),
|
||||
(mnemonic_path, 'mnemonic/wordlist'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'pkg_resources.py2_warn',
|
||||
'cryptoadvance.specter.config'
|
||||
],
|
||||
hookspath=['hooks/'],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
import hwilib
|
||||
a.datas += Tree('../udev', prefix='hwilib/udev')
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='hwibridge',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False )
|
||||
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='hwibridge')
|
||||
2
pyinstaller/requirements.txt
Normal file
2
pyinstaller/requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pyinstaller
|
||||
PyQt5
|
||||
334
pyinstaller/specter_desktop.py
Normal file
334
pyinstaller/specter_desktop.py
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction, \
|
||||
QDialog, QDialogButtonBox, QVBoxLayout, QRadioButton, QLineEdit
|
||||
from PyQt5.QtCore import QRunnable, QThreadPool, QSettings
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import json
|
||||
import platform
|
||||
from cryptoadvance.specter.config import DATA_FOLDER
|
||||
from cryptoadvance.specter.helpers import deep_update
|
||||
|
||||
running = True
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
is_specterd_running = False
|
||||
specterd_thread = None
|
||||
settings = QSettings('cryptoadvance', 'specter')
|
||||
wait_for_specterd_process = None
|
||||
|
||||
|
||||
def resource_path(relative_path):
|
||||
try:
|
||||
base_path = sys._MEIPASS
|
||||
except Exception:
|
||||
base_path = os.path.abspath(".")
|
||||
return os.path.join(base_path, relative_path)
|
||||
|
||||
|
||||
class SpecterPreferencesDialog(QDialog):
|
||||
global settings
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SpecterPreferencesDialog, self).__init__(*args, **kwargs)
|
||||
|
||||
self.setWindowTitle("Specter Preferences")
|
||||
self.layout = QVBoxLayout()
|
||||
|
||||
QBtn = QDialogButtonBox.Save | QDialogButtonBox.Cancel
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QBtn)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
|
||||
# Mode setting
|
||||
self.mode_local = QRadioButton("Run Local Specter Server")
|
||||
self.mode_local.toggled.connect(self.toggle_mode)
|
||||
|
||||
self.mode_remote = QRadioButton("Use a Remote Specter Server")
|
||||
self.mode_remote.toggled.connect(self.toggle_mode)
|
||||
|
||||
self.specter_url = QLineEdit(
|
||||
placeholderText="Please enter the remote Specter URL"
|
||||
)
|
||||
|
||||
is_remote_mode = settings.value(
|
||||
'remote_mode',
|
||||
defaultValue=False,
|
||||
type=bool
|
||||
)
|
||||
if is_remote_mode:
|
||||
self.mode_remote.setChecked(True)
|
||||
else:
|
||||
self.mode_local.setChecked(True)
|
||||
self.specter_url.hide()
|
||||
|
||||
settings.setValue('remote_mode_temp', is_remote_mode)
|
||||
remote_specter_url = settings.value(
|
||||
'specter_url',
|
||||
defaultValue='',
|
||||
type=str
|
||||
) if is_remote_mode else ''
|
||||
settings.setValue('specter_url_temp', remote_specter_url)
|
||||
self.specter_url.setText(remote_specter_url)
|
||||
self.specter_url.textChanged.connect(
|
||||
lambda: settings.setValue(
|
||||
'specter_url_temp',
|
||||
self.specter_url.text()
|
||||
)
|
||||
)
|
||||
|
||||
self.layout.addWidget(self.mode_local)
|
||||
self.layout.addWidget(self.mode_remote)
|
||||
self.layout.addWidget(self.specter_url)
|
||||
|
||||
self.layout.addWidget(self.buttonBox)
|
||||
self.resize(500, 180)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def toggle_mode(self):
|
||||
if self.mode_local.isChecked():
|
||||
settings.setValue('remote_mode_temp', False)
|
||||
self.specter_url.hide()
|
||||
else:
|
||||
settings.setValue('remote_mode_temp', True)
|
||||
self.specter_url.show()
|
||||
|
||||
|
||||
class ProcessRunnable(QRunnable):
|
||||
def __init__(self, target, args):
|
||||
QRunnable.__init__(self)
|
||||
self.t = target
|
||||
self.args = args
|
||||
|
||||
def run(self):
|
||||
self.t(*self.args)
|
||||
|
||||
def start(self):
|
||||
QThreadPool.globalInstance().start(self)
|
||||
|
||||
|
||||
def wait_for_specterd(menu):
|
||||
global specterd_thread, running
|
||||
start_specterd_menu = menu.actions()[0]
|
||||
start_specterd_menu.setEnabled(False)
|
||||
start_specterd_menu.setText('Starting up Specter{} daemon...'.format(
|
||||
' HWIBridge' if settings.value(
|
||||
"remote_mode", defaultValue=False, type=bool
|
||||
) else ''
|
||||
))
|
||||
while running:
|
||||
line = specterd_thread.stdout.readline()
|
||||
if b'Serving Flask app' in line:
|
||||
print("* Started Specter daemon...")
|
||||
start_specterd_menu.setText('Specter{} daemon is running'.format(
|
||||
' HWIBridge' if settings.value(
|
||||
"remote_mode", defaultValue=False, type=bool
|
||||
) else ''
|
||||
))
|
||||
toggle_specterd_status(menu)
|
||||
open_specter_window()
|
||||
return
|
||||
elif b'Failed' in line or b'Error' in line:
|
||||
start_specterd_menu.setText('Start Specter daemon'.format(
|
||||
' HWIBridge' if settings.value(
|
||||
"remote_mode", defaultValue=False, type=bool
|
||||
) else ''
|
||||
))
|
||||
start_specterd_menu.setEnabled(True)
|
||||
return
|
||||
|
||||
|
||||
def run_specterd(menu):
|
||||
global specterd_thread, wait_for_specterd_process
|
||||
try:
|
||||
specterd_command = [
|
||||
os.path.join(
|
||||
resource_path('specterd'),
|
||||
'{}{}'.format(
|
||||
'hwibridge' if settings.value(
|
||||
"remote_mode", defaultValue=False, type=bool
|
||||
) else 'specterd',
|
||||
'.exe' if platform.system() == "Windows" else '')
|
||||
)
|
||||
]
|
||||
specterd_thread = subprocess.Popen(
|
||||
specterd_command,
|
||||
stdout=subprocess.PIPE
|
||||
)
|
||||
wait_for_specterd_process = ProcessRunnable(
|
||||
target=wait_for_specterd,
|
||||
args=(menu, )
|
||||
)
|
||||
wait_for_specterd_process.start()
|
||||
except Exception as e:
|
||||
print("* Failed to start Specter daemon {}".format(e))
|
||||
|
||||
|
||||
def stop_specterd(menu):
|
||||
global specterd_thread
|
||||
try:
|
||||
if specterd_thread:
|
||||
specterd_thread.terminate()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("* Stopped Specter daemon")
|
||||
toggle_specterd_status(menu)
|
||||
|
||||
|
||||
def open_specter_window():
|
||||
global settings
|
||||
webbrowser.open(settings.value("specter_url", type=str), new=1)
|
||||
|
||||
|
||||
def toggle_specterd_status(menu):
|
||||
global is_specterd_running
|
||||
start_specterd_menu = menu.actions()[0]
|
||||
stop_specterd_menu = menu.actions()[1]
|
||||
open_specter_menu = menu.actions()[2]
|
||||
|
||||
if is_specterd_running:
|
||||
start_specterd_menu.setEnabled(False)
|
||||
stop_specterd_menu.setEnabled(True)
|
||||
open_specter_menu.setEnabled(True)
|
||||
else:
|
||||
start_specterd_menu.setText('Start Specter{} daemon'.format(
|
||||
' HWIBridge' if settings.value(
|
||||
"remote_mode", defaultValue=False, type=bool
|
||||
) else ''
|
||||
))
|
||||
start_specterd_menu.setEnabled(True)
|
||||
stop_specterd_menu.setEnabled(False)
|
||||
open_specter_menu.setEnabled(False)
|
||||
is_specterd_running = not is_specterd_running
|
||||
|
||||
|
||||
def quit_specter(app):
|
||||
global running
|
||||
running = False
|
||||
if specterd_thread:
|
||||
specterd_thread.terminate()
|
||||
app.quit()
|
||||
|
||||
|
||||
def open_settings():
|
||||
global settings
|
||||
dlg = SpecterPreferencesDialog()
|
||||
if dlg.exec_():
|
||||
is_remote_mode = settings.value(
|
||||
'remote_mode_temp',
|
||||
defaultValue=False,
|
||||
type=bool
|
||||
)
|
||||
settings.setValue(
|
||||
'remote_mode',
|
||||
is_remote_mode
|
||||
)
|
||||
|
||||
specter_url_temp = settings.value(
|
||||
'specter_url_temp',
|
||||
defaultValue='http://localhost:25441/',
|
||||
type=str
|
||||
)
|
||||
|
||||
settings.setValue(
|
||||
'specter_url',
|
||||
specter_url_temp if is_remote_mode else 'http://localhost:25441/'
|
||||
)
|
||||
|
||||
hwibridge_settings_path = os.path.join(
|
||||
os.path.expanduser(DATA_FOLDER),
|
||||
"hwi_bridge_config.json"
|
||||
)
|
||||
|
||||
if is_remote_mode:
|
||||
config = {
|
||||
'whitelisted_domains': 'http://127.0.0.1:25441/'
|
||||
}
|
||||
if os.path.isfile(hwibridge_settings_path):
|
||||
with open(hwibridge_settings_path, "r") as f:
|
||||
file_config = json.loads(f.read())
|
||||
deep_update(config, file_config)
|
||||
with open(hwibridge_settings_path, "w") as f:
|
||||
if 'whitelisted_domains' in config:
|
||||
whitelisted_domains = ''
|
||||
if specter_url_temp not in config[
|
||||
'whitelisted_domains'
|
||||
].split():
|
||||
config['whitelisted_domains'] += ' ' + specter_url_temp
|
||||
for url in config['whitelisted_domains'].split():
|
||||
if not url.endswith("/") and url != '*':
|
||||
# make sure the url end with a "/"
|
||||
url += "/"
|
||||
whitelisted_domains += url.strip() + '\n'
|
||||
config['whitelisted_domains'] = whitelisted_domains
|
||||
f.write(json.dumps(config, indent=4))
|
||||
# TODO: Add PORT setting
|
||||
|
||||
|
||||
def init_desktop_app():
|
||||
global settings
|
||||
app = QApplication([])
|
||||
app.setQuitOnLastWindowClosed(False)
|
||||
|
||||
# Create the icon
|
||||
icon = QIcon(os.path.join(
|
||||
resource_path('specterd'),
|
||||
'static/img/icon.png'
|
||||
))
|
||||
|
||||
# Create the tray
|
||||
tray = QSystemTrayIcon()
|
||||
tray.setIcon(icon)
|
||||
tray.setVisible(True)
|
||||
|
||||
# Create the menu
|
||||
menu = QMenu()
|
||||
start_specterd_menu = QAction("Start Specter{} daemon".format(
|
||||
' HWIBridge' if settings.value(
|
||||
"remote_mode", defaultValue=False, type=bool
|
||||
) else ''
|
||||
))
|
||||
stop_specterd_menu = QAction("Stop Specter daemon")
|
||||
open_specter_menu = QAction("Open Specter")
|
||||
|
||||
start_specterd_menu.triggered.connect(lambda: run_specterd(menu))
|
||||
menu.addAction(start_specterd_menu)
|
||||
|
||||
stop_specterd_menu.triggered.connect(lambda: stop_specterd(menu))
|
||||
menu.addAction(stop_specterd_menu)
|
||||
|
||||
open_specter_menu.triggered.connect(open_specter_window)
|
||||
menu.addAction(open_specter_menu)
|
||||
|
||||
toggle_specterd_status(menu)
|
||||
|
||||
open_settings_menu = QAction("Preferences")
|
||||
open_settings_menu.triggered.connect(open_settings)
|
||||
menu.addAction(open_settings_menu)
|
||||
|
||||
# Add a Quit option to the menu.
|
||||
quit = QAction("Quit")
|
||||
quit.triggered.connect(lambda: quit_specter(app))
|
||||
menu.addAction(quit)
|
||||
|
||||
# Add the menu to the tray
|
||||
tray.setContextMenu(menu)
|
||||
|
||||
app.setWindowIcon(icon)
|
||||
|
||||
# Setup settings
|
||||
if settings.value('first_time', defaultValue=True, type=bool):
|
||||
settings.setValue('first_time', False)
|
||||
settings.setValue('remote_mode', False)
|
||||
settings.setValue('specter_url', 'http://localhost:25441/')
|
||||
open_settings()
|
||||
|
||||
run_specterd(menu)
|
||||
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_desktop_app()
|
||||
117
pyinstaller/specter_desktop.spec
Normal file
117
pyinstaller/specter_desktop.spec
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
import platform
|
||||
import subprocess
|
||||
import mnemonic, os, sys
|
||||
|
||||
mnemonic_path = os.path.join(mnemonic.__path__[0], "wordlist")
|
||||
|
||||
block_cipher = None
|
||||
|
||||
binaries = []
|
||||
if platform.system() == 'Windows':
|
||||
binaries = [("./windll/libusb-1.0.dll", ".")]
|
||||
elif platform.system() == 'Linux':
|
||||
if platform.processor() == 'aarch64': #ARM 64 bit
|
||||
binaries = [("/lib/aarch64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
else:
|
||||
binaries = [("/lib/x86_64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
elif platform.system() == 'Darwin':
|
||||
find_brew_libusb_proc = subprocess.Popen(['brew', '--prefix', 'libusb'], stdout=subprocess.PIPE)
|
||||
libusb_path = find_brew_libusb_proc.communicate()[0]
|
||||
binaries = [(libusb_path.rstrip().decode() + "/lib/libusb-1.0.dylib", ".")]
|
||||
|
||||
a = Analysis(['specter_desktop.py'],
|
||||
binaries=binaries,
|
||||
datas=[('../src/cryptoadvance/specter/templates', 'templates'),
|
||||
('../src/cryptoadvance/specter/static', 'static'),
|
||||
('./specterd', 'specterd'),
|
||||
(mnemonic_path, 'mnemonic/wordlist'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'pkg_resources.py2_warn',
|
||||
'cryptoadvance.specter.config'
|
||||
],
|
||||
hookspath=['hooks/'],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
import hwilib
|
||||
a.datas += Tree('../udev', prefix='hwilib/udev')
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='Specter',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True )
|
||||
|
||||
app = BUNDLE(
|
||||
exe,
|
||||
name='Specter.app',
|
||||
icon='../src/cryptoadvance/specter/static/img/icon.icns',
|
||||
bundle_identifier=None,
|
||||
info_plist={
|
||||
'NSPrincipleClass': 'NSApplication',
|
||||
'NSAppleScriptEnabled': False,
|
||||
'NSHighResolutionCapable': 'True',
|
||||
'NSRequiresAquaSystemAppearance': 'True',
|
||||
'LSUIElement': 1
|
||||
}
|
||||
)
|
||||
if sys.platform == 'linux':
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
name='Specter',
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
icon='../src/cryptoadvance/specter/static/img/icon.ico'
|
||||
)
|
||||
|
||||
if sys.platform == 'win32' or sys.platform == 'win64':
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='specter_desktop',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
icon='../src/cryptoadvance/specter/static/img/icon.ico' )
|
||||
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
console=False,
|
||||
name='specter_desktop')
|
||||
|
||||
65
pyinstaller/specterd_onedir.spec
Normal file
65
pyinstaller/specterd_onedir.spec
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
import platform
|
||||
import subprocess
|
||||
import mnemonic, os
|
||||
|
||||
mnemonic_path = os.path.join(mnemonic.__path__[0], "wordlist")
|
||||
|
||||
block_cipher = None
|
||||
|
||||
binaries = []
|
||||
if platform.system() == 'Windows':
|
||||
binaries = [("./windll/libusb-1.0.dll", ".")]
|
||||
elif platform.system() == 'Linux':
|
||||
if platform.processor() == 'aarch64': #ARM 64 bit
|
||||
binaries = [("/lib/aarch64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
else:
|
||||
binaries = [("/lib/x86_64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
elif platform.system() == 'Darwin':
|
||||
find_brew_libusb_proc = subprocess.Popen(['brew', '--prefix', 'libusb'], stdout=subprocess.PIPE)
|
||||
libusb_path = find_brew_libusb_proc.communicate()[0]
|
||||
binaries = [(libusb_path.rstrip().decode() + "/lib/libusb-1.0.dylib", ".")]
|
||||
|
||||
a = Analysis(['specterd.py'],
|
||||
binaries=binaries,
|
||||
datas=[('../src/cryptoadvance/specter/templates', 'templates'),
|
||||
('../src/cryptoadvance/specter/static', 'static'),
|
||||
(mnemonic_path, 'mnemonic/wordlist'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'pkg_resources.py2_warn',
|
||||
'cryptoadvance.specter.config'
|
||||
],
|
||||
hookspath=['hooks/'],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
import hwilib
|
||||
a.datas += Tree('../udev', prefix='hwilib/udev')
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='specterd',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False )
|
||||
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='specterd')
|
||||
|
|
@ -11,7 +11,8 @@ import docker
|
|||
|
||||
from .bitcoind import (BitcoindDockerController,
|
||||
fetch_wallet_addresses_for_mining)
|
||||
from .server import DATA_FOLDER, create_app, init_app
|
||||
from .server import create_app, init_app
|
||||
from .config import DATA_FOLDER
|
||||
|
||||
from os import path
|
||||
import signal
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ except ImportError:
|
|||
# Python 3
|
||||
import configparser
|
||||
|
||||
DATA_FOLDER = "~/.specter"
|
||||
|
||||
# BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# Loading env-vars from .flaskenv (4 levels above this file)
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ from .helpers import hwi_get_config
|
|||
from .specter import Specter
|
||||
from .hwi_server import hwi_server
|
||||
from .user import User
|
||||
from .config import DATA_FOLDER
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
env_path = Path('.') / '.flaskenv'
|
||||
load_dotenv(env_path)
|
||||
|
||||
DATA_FOLDER = "~/.specter"
|
||||
|
||||
def create_app(config="cryptoadvance.specter.config.DevelopmentConfig"):
|
||||
# Enables injection of a different config via Env-Variable
|
||||
|
|
@ -33,7 +33,11 @@ def create_app(config="cryptoadvance.specter.config.DevelopmentConfig"):
|
|||
logger.info("pyinstaller based instance running in {}".format(sys._MEIPASS))
|
||||
app = Flask(__name__, template_folder=template_folder, static_folder=static_folder)
|
||||
else:
|
||||
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||
app = Flask(
|
||||
__name__,
|
||||
template_folder="templates",
|
||||
static_folder="static"
|
||||
)
|
||||
app.config.from_object(config)
|
||||
return app
|
||||
|
||||
|
|
@ -43,21 +47,22 @@ def init_app(app, hwibridge=False, specter=None):
|
|||
app.logger.info("Initializing QRcode")
|
||||
# Login via Flask-Login
|
||||
app.logger.info("Initializing LoginManager")
|
||||
if specter == None:
|
||||
if specter is None:
|
||||
# the default. If not None, then it got injected for testing
|
||||
app.logger.info("Initializing Specter")
|
||||
specter = Specter(DATA_FOLDER)
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app) # Enable Login
|
||||
login_manager.login_view = "login" # Enable redirects if unauthorized
|
||||
login_manager.init_app(app) # Enable Login
|
||||
login_manager.login_view = "login" # Enable redirects if unauthorized
|
||||
|
||||
@login_manager.user_loader
|
||||
def user_loader(id):
|
||||
return User.get_user(specter, id)
|
||||
|
||||
def login(id):
|
||||
login_user(user_loader(id))
|
||||
|
||||
|
||||
app.login = login
|
||||
# Attach specter instance so child views (e.g. hwi) can access it
|
||||
app.specter = specter
|
||||
|
|
@ -71,7 +76,7 @@ def init_app(app, hwibridge=False, specter=None):
|
|||
if not hwibridge:
|
||||
with app.app_context():
|
||||
from cryptoadvance.specter import controller
|
||||
if app.config.get("TESTING") and len(app.view_functions) <=3 :
|
||||
if app.config.get("TESTING") and len(app.view_functions) <= 3:
|
||||
# Need to force a reload as otherwise the import is skipped
|
||||
# in pytest, the app is created anew for ech test
|
||||
# But we shouldn't do that if not necessary as this would result in
|
||||
|
|
@ -84,6 +89,7 @@ def init_app(app, hwibridge=False, specter=None):
|
|||
return redirect('/hwi/settings')
|
||||
return app
|
||||
|
||||
|
||||
def create_and_init():
|
||||
''' This method can be used to fill the FLASK_APP-env variable like
|
||||
export FLASK_APP="src/cryptoadvance/specter/server:create_and_init()"
|
||||
|
|
|
|||
BIN
src/cryptoadvance/specter/static/img/icon.icns
Normal file
BIN
src/cryptoadvance/specter/static/img/icon.icns
Normal file
Binary file not shown.
BIN
src/cryptoadvance/specter/static/img/icon.ico
Normal file
BIN
src/cryptoadvance/specter/static/img/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
Loading…
Add table
Add a link
Reference in a new issue