db4e 0.16.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- db4e/App.py +168 -0
- db4e/Db4E.tcss +104 -0
- db4e/Messages/NavLeafSelected.py +21 -0
- db4e/Messages/RefreshNavPane.py +19 -0
- db4e/Messages/SubmitFormData.py +19 -0
- db4e/Messages/SwitchPane.py +21 -0
- db4e/Messages/UpdateTopBar.py +20 -0
- db4e/Modules/ConfigMgr.py +112 -0
- db4e/Modules/DbMgr.py +94 -0
- db4e/Modules/DeploymentMgr.py +74 -0
- db4e/Modules/InstallMgr.py +272 -0
- db4e/Modules/PaneCatalogue.py +37 -0
- db4e/Modules/PaneMgr.py +67 -0
- db4e/Modules/__init__.py +0 -0
- db4e/Panes/Db4E.py +73 -0
- db4e/Panes/InitialSetup.py +70 -0
- db4e/Panes/InstallResults.py +36 -0
- db4e/Panes/Welcome.py +20 -0
- db4e/Templates/__init__.py +0 -0
- db4e/Templates/db/Deployment.py +122 -0
- db4e/Templates/db4e/systemd/db4e.service +18 -0
- db4e/Templates/monerod-0.18.4.0/bin/monerod +0 -0
- db4e/Templates/monerod-0.18.4.0/bin/start-monerod.sh +64 -0
- db4e/Templates/monerod-0.18.4.0/conf/monerod.ini +34 -0
- db4e/Templates/monerod-0.18.4.0/systemd/monerod@.service +21 -0
- db4e/Templates/monerod-0.18.4.0/systemd/monerod@.socket +13 -0
- db4e/Templates/p2pool-4.8/bin/p2pool +0 -0
- db4e/Templates/p2pool-4.8/bin/start-p2pool.sh +67 -0
- db4e/Templates/p2pool-4.8/conf/p2pool.ini +24 -0
- db4e/Templates/p2pool-4.8/systemd/p2pool@.service +19 -0
- db4e/Templates/p2pool-4.8/systemd/p2pool@.socket +12 -0
- db4e/Templates/xmrig-6.23.0/bin/xmrig +0 -0
- db4e/Templates/xmrig-6.23.0/conf/config.json +133 -0
- db4e/Templates/xmrig-6.23.0/systemd/xmrig@.service +18 -0
- db4e/Widgets/Clock.py +32 -0
- db4e/Widgets/DetailPane.py +38 -0
- db4e/Widgets/FormButton.py +21 -0
- db4e/Widgets/NavPane.py +58 -0
- db4e/Widgets/TopBar.py +45 -0
- db4e/Widgets/__init__.py +0 -0
- db4e/bin/db4e-backup.sh +90 -0
- db4e/bin/db4e-initial-setup.sh +85 -0
- db4e/bin/db4e-install-service.sh +43 -0
- db4e/bin/db4e-metrics.sh +47 -0
- db4e/bin/db4e-uninstall-service.sh +49 -0
- db4e-0.16.0.dist-info/LICENSE +674 -0
- db4e-0.16.0.dist-info/METADATA +31 -0
- db4e-0.16.0.dist-info/RECORD +50 -0
- db4e-0.16.0.dist-info/WHEEL +4 -0
- db4e-0.16.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Modules/InstallMgr.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os, shutil
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
import getpass
|
|
13
|
+
import subprocess
|
|
14
|
+
|
|
15
|
+
from db4e.Modules.ConfigMgr import Config
|
|
16
|
+
from db4e.Modules.DbMgr import DbMgr
|
|
17
|
+
from db4e.Modules.DeploymentMgr import DeploymentMgr
|
|
18
|
+
|
|
19
|
+
# The Mongo collection that houses the deployment records
|
|
20
|
+
DEPL_COL = 'depl'
|
|
21
|
+
|
|
22
|
+
class InstallMgr:
|
|
23
|
+
|
|
24
|
+
def __init__(self, config: Config):
|
|
25
|
+
self.ini = config
|
|
26
|
+
self.depl_mgr = DeploymentMgr(config)
|
|
27
|
+
self.db = DbMgr(config)
|
|
28
|
+
|
|
29
|
+
async def initial_setup(self, form_data: dict) -> dict:
|
|
30
|
+
# Track the progress of the initial install
|
|
31
|
+
results = []
|
|
32
|
+
# Validate the data
|
|
33
|
+
user_wallet = form_data['user_wallet']
|
|
34
|
+
db4e_group = form_data['db4e_group']
|
|
35
|
+
vendor_dir = form_data['vendor_dir']
|
|
36
|
+
|
|
37
|
+
db4e_rec = self.depl_mgr.get_deployment('db4e')
|
|
38
|
+
if db4e_rec:
|
|
39
|
+
# The Mongo record for 'db4e' exists: Assume we're doing a reinstall
|
|
40
|
+
results.append({'Db4E core': {'status': 'warn', 'msg': 'Db4E core already exists'}})
|
|
41
|
+
old_user_wallet = db4e_rec['user_wallet']
|
|
42
|
+
old_group = db4e_rec['group']
|
|
43
|
+
old_vendor_dir = db4e_rec['vendor_dir']
|
|
44
|
+
|
|
45
|
+
if user_wallet != old_user_wallet:
|
|
46
|
+
results.append({'Monero wallet': {'status': 'warn', 'msg': 'Old Monero wallet record'}})
|
|
47
|
+
db4e_rec['user_wallet'] = user_wallet
|
|
48
|
+
self.depl_mgr.update_deployent(db4e_rec)
|
|
49
|
+
results.append({'Monero wallet': {'status': 'good', 'msg': 'Updated Monero wallet'}})
|
|
50
|
+
if db4e_group != old_group:
|
|
51
|
+
results.append({'Db4E Group': {'status':'warn', 'msg': f'Old Db4E group ({old_group}) record'}})
|
|
52
|
+
os.environ['DB4E_OLD_GROUP'] = old_group
|
|
53
|
+
if vendor_dir != old_vendor_dir:
|
|
54
|
+
results.append({'Deployment directory': {'status':'warn', 'msg': f'Old deployment directory ({old_vendor_dir}) record'}})
|
|
55
|
+
else:
|
|
56
|
+
db4e_rec = self.db.get_new_rec('db4e')
|
|
57
|
+
db4e_rec['user_wallet'] = user_wallet
|
|
58
|
+
self.depl_mgr.add_deployment(db4e_rec)
|
|
59
|
+
|
|
60
|
+
# Create the vendor directory
|
|
61
|
+
if os.path.exists(vendor_dir):
|
|
62
|
+
results.append({'Deployment directory': {'status':'warn', 'msg': f'Found existing deployment directory ({vendor_dir})'}})
|
|
63
|
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
|
|
64
|
+
try:
|
|
65
|
+
backup_vendor_dir = vendor_dir + '.' + timestamp
|
|
66
|
+
os.rename(vendor_dir, backup_vendor_dir)
|
|
67
|
+
results.append({'Deployment directory': {'status': 'warn', 'msg': f'Backed up old deployment directory ({backup_vendor_dir})'}})
|
|
68
|
+
except PermissionError:
|
|
69
|
+
results.append({'Deployment directory': {'status': 'error', 'msg': f'Failed to backup old deployment directory ({backup_vendor_dir})'}})
|
|
70
|
+
return results # Abort the install
|
|
71
|
+
try:
|
|
72
|
+
os.mkdir(vendor_dir)
|
|
73
|
+
except (PermissionError, FileNotFoundError, FileExistsError) as e:
|
|
74
|
+
error_msg = f'Failed to create directory ({vendor_dir}). Make sure you '
|
|
75
|
+
error_msg += 'have permission to create the directory and that the parent '
|
|
76
|
+
error_msg += 'directory exists\n\n'
|
|
77
|
+
error_msg += f'{e}'
|
|
78
|
+
results.append({'Deployment directory': {'status': 'error', 'msg': error_msg}})
|
|
79
|
+
return results # Abort the install
|
|
80
|
+
|
|
81
|
+
# Additional config settings
|
|
82
|
+
bin_dir = self.ini.config['db4e']['bin_dir']
|
|
83
|
+
conf_dir = self.ini.config['db4e']['conf_dir']
|
|
84
|
+
db4e_dir = self.ini.config['db4e']['db4e_dir']
|
|
85
|
+
db4e_service_file = self.ini.config['db4e']['service_file']
|
|
86
|
+
initial_setup_script = self.ini.config['db4e']['setup_script']
|
|
87
|
+
log_dir = self.ini.config['db4e']['log_dir']
|
|
88
|
+
run_dir = self.ini.config['db4e']['run_dir']
|
|
89
|
+
systemd_dir = self.ini.config['db4e']['systemd_dir']
|
|
90
|
+
templates_dir = self.ini.config['db4e']['template_dir']
|
|
91
|
+
p2pool_binary = self.ini.config['p2pool']['process']
|
|
92
|
+
p2pool_service_file = self.ini.config['p2pool']['service_file']
|
|
93
|
+
p2pool_start_script = self.ini.config['p2pool']['start_script']
|
|
94
|
+
p2pool_socket_file = self.ini.config['p2pool']['socket_file']
|
|
95
|
+
p2pool_version = self.ini.config['p2pool']['version']
|
|
96
|
+
blockchain_dir = self.ini.config['monerod']['blockchain_dir']
|
|
97
|
+
monerod_binary = self.ini.config['monerod']['process']
|
|
98
|
+
monerod_service_file = self.ini.config['monerod']['service_file']
|
|
99
|
+
monerod_socket_file = self.ini.config['monerod']['socket_file']
|
|
100
|
+
monerod_start_script = self.ini.config['monerod']['start_script']
|
|
101
|
+
monerod_version = self.ini.config['monerod']['version']
|
|
102
|
+
xmrig_binary = self.ini.config['xmrig']['process']
|
|
103
|
+
xmrig_service_file = self.ini.config['xmrig']['service_file']
|
|
104
|
+
xmrig_version = self.ini.config['xmrig']['version']
|
|
105
|
+
|
|
106
|
+
# The db4e user (the account used to run Db4E)
|
|
107
|
+
db4e_user = getpass.getuser()
|
|
108
|
+
|
|
109
|
+
# db4e, P2Pool, Monero daemon and XMRig directories
|
|
110
|
+
db4e_vendor_dir = 'db4e'
|
|
111
|
+
p2pool_dir = 'p2pool-' + str(p2pool_version)
|
|
112
|
+
monerod_dir = 'monerod-' + str(monerod_version)
|
|
113
|
+
xmrig_dir = 'xmrig-' + str(xmrig_version)
|
|
114
|
+
|
|
115
|
+
# Create the vendor directories
|
|
116
|
+
os.mkdir(os.path.join(vendor_dir, blockchain_dir))
|
|
117
|
+
os.mkdir(os.path.join(vendor_dir, db4e_vendor_dir))
|
|
118
|
+
os.mkdir(os.path.join(vendor_dir, db4e_vendor_dir, conf_dir))
|
|
119
|
+
os.mkdir(os.path.join(vendor_dir, p2pool_dir))
|
|
120
|
+
os.mkdir(os.path.join(vendor_dir, p2pool_dir, bin_dir))
|
|
121
|
+
os.mkdir(os.path.join(vendor_dir, p2pool_dir, conf_dir))
|
|
122
|
+
os.mkdir(os.path.join(vendor_dir, p2pool_dir, run_dir))
|
|
123
|
+
os.mkdir(os.path.join(vendor_dir, monerod_dir))
|
|
124
|
+
os.mkdir(os.path.join(vendor_dir, monerod_dir, bin_dir))
|
|
125
|
+
os.mkdir(os.path.join(vendor_dir, monerod_dir, conf_dir))
|
|
126
|
+
os.mkdir(os.path.join(vendor_dir, monerod_dir, run_dir))
|
|
127
|
+
os.mkdir(os.path.join(vendor_dir, monerod_dir, log_dir))
|
|
128
|
+
os.mkdir(os.path.join(vendor_dir, xmrig_dir))
|
|
129
|
+
os.mkdir(os.path.join(vendor_dir, xmrig_dir, bin_dir))
|
|
130
|
+
os.mkdir(os.path.join(vendor_dir, xmrig_dir, conf_dir))
|
|
131
|
+
|
|
132
|
+
# The Templates directory
|
|
133
|
+
tmpl_dir = os.path.join(os.path.dirname(__file__), '..', templates_dir)
|
|
134
|
+
# Fully qualifed directories
|
|
135
|
+
fq_db4e_dir = os.path.join(vendor_dir, db4e_vendor_dir)
|
|
136
|
+
fq_p2pool_dir = os.path.join(vendor_dir, p2pool_dir)
|
|
137
|
+
fq_monerod_dir = os.path.join(vendor_dir, monerod_dir)
|
|
138
|
+
fq_xmrig_dir = os.path.join(vendor_dir, xmrig_dir)
|
|
139
|
+
|
|
140
|
+
# Templates for the db4e, Monero daemon and P2pool services
|
|
141
|
+
fq_db4e_service_file = os.path.join(tmpl_dir, 'db4e', systemd_dir, db4e_service_file)
|
|
142
|
+
fq_p2pool_service_file = os.path.join(tmpl_dir, p2pool_dir, systemd_dir, p2pool_service_file)
|
|
143
|
+
fq_p2pool_socket_file = os.path.join(tmpl_dir, p2pool_dir, systemd_dir, p2pool_socket_file)
|
|
144
|
+
fq_monerod_service_file = os.path.join(tmpl_dir, monerod_dir, systemd_dir, monerod_service_file)
|
|
145
|
+
fq_monerod_socket_file = os.path.join(tmpl_dir, monerod_dir, systemd_dir, monerod_socket_file)
|
|
146
|
+
fq_xmrig_service_file = os.path.join(tmpl_dir, xmrig_dir, systemd_dir, xmrig_service_file)
|
|
147
|
+
|
|
148
|
+
# P2Pool, Monerod daemon, XMRig binaries and start-scripts
|
|
149
|
+
fq_p2pool = os.path.join(tmpl_dir, p2pool_dir, bin_dir, p2pool_binary)
|
|
150
|
+
fq_p2pool_start_script = os.path.join(tmpl_dir, p2pool_dir, bin_dir, p2pool_start_script)
|
|
151
|
+
fq_monerod = os.path.join(tmpl_dir, monerod_dir, bin_dir, monerod_binary)
|
|
152
|
+
fq_monerod_start_script = os.path.join(tmpl_dir, monerod_dir, bin_dir, monerod_start_script)
|
|
153
|
+
fq_xmrig = os.path.join(tmpl_dir, xmrig_dir, bin_dir, xmrig_binary)
|
|
154
|
+
|
|
155
|
+
# Temp directory to house the systemd service files
|
|
156
|
+
tmp_dir = os.path.join('/tmp', 'db4e')
|
|
157
|
+
if os.path.exists(tmp_dir):
|
|
158
|
+
shutil.rmtree(tmp_dir)
|
|
159
|
+
os.mkdir(tmp_dir)
|
|
160
|
+
|
|
161
|
+
# Update the db4e service template with deployment values
|
|
162
|
+
fq_db4e_dir = os.path.join(vendor_dir, )
|
|
163
|
+
placeholders = {
|
|
164
|
+
'DB4E_USER': db4e_user,
|
|
165
|
+
'DB4E_GROUP': db4e_group,
|
|
166
|
+
'DB4E_DIR': fq_db4e_dir,
|
|
167
|
+
}
|
|
168
|
+
with open(fq_db4e_service_file, 'r') as f:
|
|
169
|
+
service_contents = f.read()
|
|
170
|
+
for key, val in placeholders.items():
|
|
171
|
+
service_contents = service_contents.replace(f'[[{key}]]', str(val))
|
|
172
|
+
tmp_service_file = os.path.join(tmp_dir, db4e_service_file)
|
|
173
|
+
with open(tmp_service_file, 'w') as f:
|
|
174
|
+
f.write(service_contents)
|
|
175
|
+
|
|
176
|
+
# Update the P2Pool service templates with deployment values
|
|
177
|
+
placeholders = {
|
|
178
|
+
'P2POOL_DIR': fq_p2pool_dir,
|
|
179
|
+
'DB4E_USER': db4e_user,
|
|
180
|
+
'DB4E_GROUP': db4e_group,
|
|
181
|
+
}
|
|
182
|
+
with open(fq_p2pool_service_file, 'r') as f:
|
|
183
|
+
service_contents = f.read()
|
|
184
|
+
for key, val in placeholders.items():
|
|
185
|
+
service_contents = service_contents.replace(f'[[{key}]]', str(val))
|
|
186
|
+
tmp_service_file = os.path.join(tmp_dir, p2pool_service_file)
|
|
187
|
+
with open(tmp_service_file, 'w') as f:
|
|
188
|
+
f.write(service_contents)
|
|
189
|
+
with open(fq_p2pool_socket_file, 'r') as f:
|
|
190
|
+
service_contents = f.read()
|
|
191
|
+
for key, val in placeholders.items():
|
|
192
|
+
service_contents = service_contents.replace(f'[[{key}]]', str(val))
|
|
193
|
+
tmp_service_file = os.path.join(tmp_dir, p2pool_socket_file)
|
|
194
|
+
with open(tmp_service_file, 'w') as f:
|
|
195
|
+
f.write(service_contents)
|
|
196
|
+
|
|
197
|
+
# Update the Monero daemon service templates with deployment values
|
|
198
|
+
placeholders = {
|
|
199
|
+
'MONEROD_DIR': fq_monerod_dir,
|
|
200
|
+
'DB4E_USER': db4e_user,
|
|
201
|
+
'DB4E_GROUP': db4e_group,
|
|
202
|
+
}
|
|
203
|
+
with open(fq_monerod_service_file, 'r') as f:
|
|
204
|
+
service_contents = f.read()
|
|
205
|
+
for key, val in placeholders.items():
|
|
206
|
+
service_contents = service_contents.replace(f'[[{key}]]', str(val))
|
|
207
|
+
tmp_service_file = os.path.join(tmp_dir, monerod_service_file)
|
|
208
|
+
with open(tmp_service_file, 'w') as f:
|
|
209
|
+
f.write(service_contents)
|
|
210
|
+
with open(fq_monerod_socket_file, 'r') as f:
|
|
211
|
+
service_contents = f.read()
|
|
212
|
+
for key, val in placeholders.items():
|
|
213
|
+
service_contents = service_contents.replace(f'[[{key}]]', str(val))
|
|
214
|
+
tmp_service_file = os.path.join(tmp_dir, monerod_socket_file)
|
|
215
|
+
with open(tmp_service_file, 'w') as f:
|
|
216
|
+
f.write(service_contents)
|
|
217
|
+
|
|
218
|
+
# Update the XMRig miner service template with deployment values
|
|
219
|
+
placeholders = {
|
|
220
|
+
'XMRIG_DIR': fq_xmrig_dir,
|
|
221
|
+
'DB4E_USER': db4e_user,
|
|
222
|
+
'DB4E_GROUP': db4e_group,
|
|
223
|
+
}
|
|
224
|
+
with open(fq_xmrig_service_file, 'r') as f:
|
|
225
|
+
service_contents = f.read()
|
|
226
|
+
for key, val in placeholders.items():
|
|
227
|
+
service_contents = service_contents.replace(f'[[{key}]]', str(val))
|
|
228
|
+
tmp_service_file = os.path.join(tmp_dir, xmrig_service_file)
|
|
229
|
+
with open(tmp_service_file, 'w') as f:
|
|
230
|
+
f.write(service_contents)
|
|
231
|
+
|
|
232
|
+
# Copy in the Monero daemon, P2Pool and XMRig binaries and startup scripts
|
|
233
|
+
shutil.copy(fq_p2pool, os.path.join(vendor_dir, p2pool_dir, bin_dir))
|
|
234
|
+
shutil.copy(fq_p2pool_start_script, os.path.join(vendor_dir, p2pool_dir, bin_dir))
|
|
235
|
+
shutil.copy(fq_monerod, os.path.join(vendor_dir, monerod_dir, bin_dir))
|
|
236
|
+
shutil.copy(fq_monerod_start_script, os.path.join(vendor_dir, monerod_dir, bin_dir))
|
|
237
|
+
shutil.copy(fq_xmrig, os.path.join(vendor_dir, xmrig_dir, bin_dir))
|
|
238
|
+
|
|
239
|
+
# Run the bin/db4e-installer.sh
|
|
240
|
+
db4e_install_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
|
241
|
+
fq_initial_setup = os.path.join(db4e_install_dir, bin_dir, initial_setup_script)
|
|
242
|
+
try:
|
|
243
|
+
cmd_result = subprocess.run(
|
|
244
|
+
['sudo', fq_initial_setup, db4e_dir, db4e_user, db4e_group, vendor_dir],
|
|
245
|
+
stdout=subprocess.PIPE,
|
|
246
|
+
stderr=subprocess.PIPE,
|
|
247
|
+
input=b"",
|
|
248
|
+
timeout=10)
|
|
249
|
+
stdout = cmd_result.stdout.decode().strip()
|
|
250
|
+
stderr = cmd_result.stderr.decode().strip()
|
|
251
|
+
|
|
252
|
+
# Check the return code
|
|
253
|
+
if cmd_result.returncode != 0:
|
|
254
|
+
results.append({'Db4E core': {'status': 'error', 'msg': f'Service install failed.\n\n{stderr}'}})
|
|
255
|
+
return results
|
|
256
|
+
|
|
257
|
+
installer_output = f'{stdout}'
|
|
258
|
+
results.append({'Db4E core': {'status': 'good', 'msg': installer_output}})
|
|
259
|
+
shutil.rmtree(tmp_dir)
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
results.append({'Db4E core': {'status': 'error', 'msg': f'Fatal error: {e}'}})
|
|
263
|
+
|
|
264
|
+
# Build the db4e deployment record
|
|
265
|
+
db4e_rec['enable'] = True
|
|
266
|
+
db4e_rec['group'] = db4e_group
|
|
267
|
+
db4e_rec['install_dir'] = db4e_install_dir
|
|
268
|
+
db4e_rec['user'] = db4e_user
|
|
269
|
+
db4e_rec['vendor_dir'] = vendor_dir
|
|
270
|
+
# Update the repo deployment record
|
|
271
|
+
self.depl_mgr.update_deployent(db4e_rec)
|
|
272
|
+
return results
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Modules/PaneCatalogue.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from textual.containers import Container
|
|
11
|
+
|
|
12
|
+
from db4e.Panes.Welcome import Welcome
|
|
13
|
+
from db4e.Panes.InitialSetup import InitialSetup
|
|
14
|
+
from db4e.Panes.InstallResults import InstallResults
|
|
15
|
+
from db4e.Panes.Db4E import Db4E
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
REGISTRY = {
|
|
19
|
+
"Db4E": (Db4E, "Database 4 Everything", "Db4E Core"),
|
|
20
|
+
"InitialSetup": (InitialSetup, "Database 4 Everything", "Initial Setup"),
|
|
21
|
+
"InstallResults": (InstallResults, "Database 4 Everything", "Install Results"),
|
|
22
|
+
"Welcome": (Welcome, "Database 4 Everything", "Welcome"),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class PaneCatalogue:
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self.registry = REGISTRY
|
|
29
|
+
|
|
30
|
+
def get_pane(self, pane_name: str, pane_data=None) -> Container:
|
|
31
|
+
pane_class, _, _ = self.registry[pane_name]
|
|
32
|
+
print(f"PaneCatalogue:get_pane(): {pane_name}")
|
|
33
|
+
return pane_class(id=pane_name, data=pane_data) if pane_data else pane_class(id=pane_name)
|
|
34
|
+
|
|
35
|
+
def get_metadata(self, pane_name: str) -> tuple[str, str]:
|
|
36
|
+
_, component, msg = self.registry.get(pane_name, (None, "", ""))
|
|
37
|
+
return component, msg
|
db4e/Modules/PaneMgr.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Modules/PaneMgr.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from textual.css.query import NoMatches
|
|
12
|
+
from textual.widget import Widget
|
|
13
|
+
from textual.widgets import ContentSwitcher
|
|
14
|
+
from textual.reactive import reactive
|
|
15
|
+
|
|
16
|
+
from db4e.Modules.ConfigMgr import Config
|
|
17
|
+
from db4e.Modules.PaneCatalogue import PaneCatalogue
|
|
18
|
+
from db4e.Messages.UpdateTopBar import UpdateTopBar
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class PaneState:
|
|
22
|
+
name: str = ""
|
|
23
|
+
data: dict = field(default_factory=dict)
|
|
24
|
+
|
|
25
|
+
class PaneMgr(Widget):
|
|
26
|
+
pane_state = reactive(PaneState(), always_update=True)
|
|
27
|
+
|
|
28
|
+
def __init__(self, config: Config, catalogue: PaneCatalogue, initialized_flag: bool):
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.config = config
|
|
31
|
+
self.catalogue = catalogue
|
|
32
|
+
self.initialized_flag = initialized_flag
|
|
33
|
+
self.panes = {}
|
|
34
|
+
|
|
35
|
+
def compose(self):
|
|
36
|
+
with ContentSwitcher(initial=self.pane_state.name, id="content_switcher"):
|
|
37
|
+
for pane_name in self.catalogue.registry:
|
|
38
|
+
# Instantiate each pane once, store a reference
|
|
39
|
+
pane = self.catalogue.get_pane(pane_name)
|
|
40
|
+
self.panes[pane_name] = pane
|
|
41
|
+
yield pane
|
|
42
|
+
|
|
43
|
+
async def on_mount(self) -> None:
|
|
44
|
+
initial = PaneState(name='Welcome' if self.initialized_flag else 'InitialSetup', data={})
|
|
45
|
+
self.set_pane(initial.name, initial.data)
|
|
46
|
+
|
|
47
|
+
def set_pane(self, name: str, data: dict | None = None):
|
|
48
|
+
self.pane_state = PaneState(name, data)
|
|
49
|
+
# If the pane supports set_data, update it with new data
|
|
50
|
+
print(f"PaneMgr:set_pane(): {name}, {data}")
|
|
51
|
+
if data and name in self.panes:
|
|
52
|
+
pane = self.panes[name]
|
|
53
|
+
if hasattr(pane, "set_data"):
|
|
54
|
+
pane.set_data(data)
|
|
55
|
+
|
|
56
|
+
def watch_pane_state(self, old: PaneState, new: PaneState):
|
|
57
|
+
try:
|
|
58
|
+
content_switcher = self.query_one("#content_switcher", ContentSwitcher)
|
|
59
|
+
except NoMatches:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
content_switcher.current = new.name
|
|
63
|
+
|
|
64
|
+
# Create a message to update the TopBar's title and sub_title
|
|
65
|
+
title, sub_title = self.catalogue.get_metadata(new.name)
|
|
66
|
+
self.post_message(UpdateTopBar(self, title=title, sub_title=sub_title))
|
|
67
|
+
|
db4e/Modules/__init__.py
ADDED
|
File without changes
|
db4e/Panes/Db4E.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Panes/Db4E.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
from textual.widgets import Label, MarkdownViewer, Input, Button
|
|
10
|
+
from textual.containers import Container, Vertical, Horizontal
|
|
11
|
+
from textual.app import ComposeResult
|
|
12
|
+
|
|
13
|
+
from db4e.Messages.SubmitFormData import SubmitFormData
|
|
14
|
+
|
|
15
|
+
STATIC_CONTENT = """Welcome to the *Database 4 Everything Db4E Core* configuration screen.
|
|
16
|
+
On this screen uou can update your *Monero wallet* and relocate the *deployment directory*.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
class Db4E(Container):
|
|
20
|
+
|
|
21
|
+
def set_data(self, db4e_rec):
|
|
22
|
+
|
|
23
|
+
rec_2_biz = {
|
|
24
|
+
'group': 'Db4E Group',
|
|
25
|
+
'install_dir': 'Install Directory',
|
|
26
|
+
'user': 'Db4E User',
|
|
27
|
+
'user_wallet': 'Monero Wallet',
|
|
28
|
+
'vendor_dir': 'Deployment Directory'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
db4e_user_name = rec_2_biz['user']
|
|
32
|
+
db4e_user = db4e_rec['user']
|
|
33
|
+
db4e_group_name = rec_2_biz['group']
|
|
34
|
+
db4e_group = db4e_rec['group']
|
|
35
|
+
install_dir_name = rec_2_biz['install_dir']
|
|
36
|
+
install_dir = db4e_rec['install_dir']
|
|
37
|
+
vendor_dir_name = rec_2_biz['vendor_dir']
|
|
38
|
+
vendor_dir = db4e_rec['vendor_dir']
|
|
39
|
+
user_wallet_name = rec_2_biz['user_wallet']
|
|
40
|
+
user_wallet = db4e_rec['user_wallet']
|
|
41
|
+
|
|
42
|
+
yield Vertical(
|
|
43
|
+
MarkdownViewer(STATIC_CONTENT, show_table_of_contents=False, classes="form_intro"),
|
|
44
|
+
|
|
45
|
+
Vertical(
|
|
46
|
+
Horizontal(
|
|
47
|
+
Label(db4e_user_name, id="db4e_user_name_label"),
|
|
48
|
+
Label(db4e_user, id="db4e_user")),
|
|
49
|
+
Horizontal(
|
|
50
|
+
Label(db4e_group_name, id="db4e_group_name_label"),
|
|
51
|
+
Label(db4e_group, id="db4e_group")),
|
|
52
|
+
Horizontal(
|
|
53
|
+
Label(install_dir_name, id="install_dir_name_label"),
|
|
54
|
+
Label(install_dir, id="install_dir")),
|
|
55
|
+
Horizontal(
|
|
56
|
+
Label(vendor_dir_name, id="vendor_dir_name_label"),
|
|
57
|
+
Input(id="db4e_vendor_dir_input", restrict=r"/[a-zA-Z0-9/_.\- ]*", value=vendor_dir, compact=True)),
|
|
58
|
+
Horizontal(
|
|
59
|
+
Label(user_wallet_name, id="user_wallet_name_label"),
|
|
60
|
+
Input(id="db4e_user_wallet_input", restrict=r"[a-zA-Z0-9]*", value=user_wallet, compact=True)),
|
|
61
|
+
id="db4e_update_form"),
|
|
62
|
+
|
|
63
|
+
Button(label="Update", id="db4e_update_button"))
|
|
64
|
+
|
|
65
|
+
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
66
|
+
form_data = {
|
|
67
|
+
"to_module": "DeploymentMgr",
|
|
68
|
+
"to_method": "update_deployment",
|
|
69
|
+
"user_wallet": self.query_one("#initial_setup_user_wallet_input", Input).value,
|
|
70
|
+
"vendor_dir": self.query_one("#initial_setup_vendor_dir_input", Input).value,
|
|
71
|
+
}
|
|
72
|
+
self.app.post_message(SubmitFormData(self, form_data))
|
|
73
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Panes/InitialSetup.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
from textual.widgets import Label, MarkdownViewer, Input, Button
|
|
10
|
+
from textual.containers import Container, Vertical, Horizontal
|
|
11
|
+
from textual.app import ComposeResult
|
|
12
|
+
|
|
13
|
+
from db4e.Messages.SubmitFormData import SubmitFormData
|
|
14
|
+
from db4e.Messages.RefreshNavPane import RefreshNavPane
|
|
15
|
+
|
|
16
|
+
#from db4e.Messages.SubmitFormData import SubmitFormData
|
|
17
|
+
|
|
18
|
+
STATIC_CONTENT = """Welcome to the *Database 4 Everything* initial setup screen.
|
|
19
|
+
|
|
20
|
+
| Field | Description | Example |
|
|
21
|
+
| -------------------- | ---------------------------------------------------|----------------- |
|
|
22
|
+
| Monero wallet | Where your mining payments will be sent | 48aTDJfRH2JLc... |
|
|
23
|
+
| Linux group | A Linux group name | db4e |
|
|
24
|
+
| Deployment directory | A directory for programs, configuration files etc. | /opt/db4e |
|
|
25
|
+
|
|
26
|
+
The *Linux group* will be created and the user who is running this program will be added. The
|
|
27
|
+
*deployment directory* will be created and *Monero*, *P2Pool* and *XMRig* will be installed
|
|
28
|
+
into this directory.
|
|
29
|
+
|
|
30
|
+
Additionally, the `/etc/sudoers` will be updated to allow Db4E to start and stop Monero, P2Pool
|
|
31
|
+
and XMRig. `Systemd` services will be added for these three elements and a *Db4E* service will also
|
|
32
|
+
be installed. Finally, the *sticky bit* will be set on the XMRig executible so it runs as root to
|
|
33
|
+
access MSRs for optimal performance.
|
|
34
|
+
|
|
35
|
+
You must have *sudo* access to the root user account. This is normally already setup in a default
|
|
36
|
+
Linux installation. You will be prompted for your password, since the installer runs as root.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
MAX_GROUP_LENGTH = 20
|
|
40
|
+
|
|
41
|
+
class InitialSetup(Container):
|
|
42
|
+
|
|
43
|
+
def compose(self) -> ComposeResult:
|
|
44
|
+
yield Vertical(
|
|
45
|
+
MarkdownViewer(STATIC_CONTENT, show_table_of_contents=False, classes="form_intro"),
|
|
46
|
+
|
|
47
|
+
Vertical(
|
|
48
|
+
Horizontal(
|
|
49
|
+
Label("Linux Group:", id="initial_setup_db4e_group_label"),
|
|
50
|
+
Input(id="initial_setup_db4e_group_input", restrict=r"[a-z0-9]*", max_length=MAX_GROUP_LENGTH, compact=True)),
|
|
51
|
+
Horizontal(
|
|
52
|
+
Label("Deployment Directory:", id="initial_setup_vendor_dir_label"),
|
|
53
|
+
Input(id="initial_setup_vendor_dir_input", restrict=r"/[a-zA-Z0-9/_.\- ]*", compact=True)),
|
|
54
|
+
Horizontal(
|
|
55
|
+
Label("Wallet:", id="initial_setup_user_wallet_label"),
|
|
56
|
+
Input(id="initial_setup_user_wallet_input", restrict=r"[a-zA-Z0-9]*", compact=True)),
|
|
57
|
+
id="initial_setup_form"),
|
|
58
|
+
|
|
59
|
+
Button(label="Proceed", id="initial_setup_button"))
|
|
60
|
+
|
|
61
|
+
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
62
|
+
form_data = {
|
|
63
|
+
"to_module": "InstallMgr",
|
|
64
|
+
"to_method": "initial_setup",
|
|
65
|
+
"user_wallet": self.query_one("#initial_setup_user_wallet_input", Input).value,
|
|
66
|
+
"db4e_group": self.query_one("#initial_setup_db4e_group_input", Input).value,
|
|
67
|
+
"vendor_dir": self.query_one("#initial_setup_vendor_dir_input", Input).value,
|
|
68
|
+
}
|
|
69
|
+
self.app.post_message(SubmitFormData(self, form_data))
|
|
70
|
+
self.app.post_message(RefreshNavPane(self))
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Panes/InstallResults.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
from rich import box
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from textual.app import ComposeResult
|
|
12
|
+
from textual.widgets import Static
|
|
13
|
+
from textual.containers import Container
|
|
14
|
+
|
|
15
|
+
from db4e.Messages.RefreshNavPane import RefreshNavPane
|
|
16
|
+
|
|
17
|
+
class InstallResults(Container):
|
|
18
|
+
|
|
19
|
+
def set_data(self, task_list):
|
|
20
|
+
|
|
21
|
+
table = Table(show_header=True, header_style="bold cyan", style="bold green", box=box.SIMPLE)
|
|
22
|
+
table.add_column("Component", width=25)
|
|
23
|
+
table.add_column("Message")
|
|
24
|
+
|
|
25
|
+
for task in task_list:
|
|
26
|
+
for category, msg_dict in task.items():
|
|
27
|
+
message = msg_dict["msg"]
|
|
28
|
+
if msg_dict["status"] == "good":
|
|
29
|
+
table.add_row(f"✅ [green]{category}[/]", f"[green]{message}[/]")
|
|
30
|
+
elif msg_dict["status"] == "warn":
|
|
31
|
+
table.add_row(f"⚠️ [yellow]{category}[/]", f"[yellow]{message}[/]")
|
|
32
|
+
elif msg_dict["status"] == "error":
|
|
33
|
+
table.add_row(f"💥 [red]{category}[/]", f"[red]{message}[/]")
|
|
34
|
+
|
|
35
|
+
self.mount(Static(table))
|
|
36
|
+
self.app.post_message(RefreshNavPane(self))
|
db4e/Panes/Welcome.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db4e/Panes/Welcome.py
|
|
3
|
+
|
|
4
|
+
Database 4 Everything
|
|
5
|
+
Author: Nadim-Daniel Ghaznavi
|
|
6
|
+
Copyright (c) 2024-2025 NadimGhaznavi <https://github.com/NadimGhaznavi/db4e>
|
|
7
|
+
License: GPL 3.0
|
|
8
|
+
"""
|
|
9
|
+
from textual.widgets import Label, Static
|
|
10
|
+
from textual.containers import Container
|
|
11
|
+
from textual.app import ComposeResult
|
|
12
|
+
from textual.message import Message
|
|
13
|
+
|
|
14
|
+
#from db4e.Messages.TopBarUpdate import TopBarUpdate
|
|
15
|
+
|
|
16
|
+
class Welcome(Container):
|
|
17
|
+
|
|
18
|
+
def compose(self) -> ComposeResult:
|
|
19
|
+
yield Label('Welcome Pane')
|
|
20
|
+
yield Static('Welcome Pane - Static')
|
|
File without changes
|