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.
Files changed (50) hide show
  1. db4e/App.py +168 -0
  2. db4e/Db4E.tcss +104 -0
  3. db4e/Messages/NavLeafSelected.py +21 -0
  4. db4e/Messages/RefreshNavPane.py +19 -0
  5. db4e/Messages/SubmitFormData.py +19 -0
  6. db4e/Messages/SwitchPane.py +21 -0
  7. db4e/Messages/UpdateTopBar.py +20 -0
  8. db4e/Modules/ConfigMgr.py +112 -0
  9. db4e/Modules/DbMgr.py +94 -0
  10. db4e/Modules/DeploymentMgr.py +74 -0
  11. db4e/Modules/InstallMgr.py +272 -0
  12. db4e/Modules/PaneCatalogue.py +37 -0
  13. db4e/Modules/PaneMgr.py +67 -0
  14. db4e/Modules/__init__.py +0 -0
  15. db4e/Panes/Db4E.py +73 -0
  16. db4e/Panes/InitialSetup.py +70 -0
  17. db4e/Panes/InstallResults.py +36 -0
  18. db4e/Panes/Welcome.py +20 -0
  19. db4e/Templates/__init__.py +0 -0
  20. db4e/Templates/db/Deployment.py +122 -0
  21. db4e/Templates/db4e/systemd/db4e.service +18 -0
  22. db4e/Templates/monerod-0.18.4.0/bin/monerod +0 -0
  23. db4e/Templates/monerod-0.18.4.0/bin/start-monerod.sh +64 -0
  24. db4e/Templates/monerod-0.18.4.0/conf/monerod.ini +34 -0
  25. db4e/Templates/monerod-0.18.4.0/systemd/monerod@.service +21 -0
  26. db4e/Templates/monerod-0.18.4.0/systemd/monerod@.socket +13 -0
  27. db4e/Templates/p2pool-4.8/bin/p2pool +0 -0
  28. db4e/Templates/p2pool-4.8/bin/start-p2pool.sh +67 -0
  29. db4e/Templates/p2pool-4.8/conf/p2pool.ini +24 -0
  30. db4e/Templates/p2pool-4.8/systemd/p2pool@.service +19 -0
  31. db4e/Templates/p2pool-4.8/systemd/p2pool@.socket +12 -0
  32. db4e/Templates/xmrig-6.23.0/bin/xmrig +0 -0
  33. db4e/Templates/xmrig-6.23.0/conf/config.json +133 -0
  34. db4e/Templates/xmrig-6.23.0/systemd/xmrig@.service +18 -0
  35. db4e/Widgets/Clock.py +32 -0
  36. db4e/Widgets/DetailPane.py +38 -0
  37. db4e/Widgets/FormButton.py +21 -0
  38. db4e/Widgets/NavPane.py +58 -0
  39. db4e/Widgets/TopBar.py +45 -0
  40. db4e/Widgets/__init__.py +0 -0
  41. db4e/bin/db4e-backup.sh +90 -0
  42. db4e/bin/db4e-initial-setup.sh +85 -0
  43. db4e/bin/db4e-install-service.sh +43 -0
  44. db4e/bin/db4e-metrics.sh +47 -0
  45. db4e/bin/db4e-uninstall-service.sh +49 -0
  46. db4e-0.16.0.dist-info/LICENSE +674 -0
  47. db4e-0.16.0.dist-info/METADATA +31 -0
  48. db4e-0.16.0.dist-info/RECORD +50 -0
  49. db4e-0.16.0.dist-info/WHEEL +4 -0
  50. 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
@@ -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
+
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