kch123 analysis, copy-trade sim, monitoring, admin panel todos, nginx proxy

This commit is contained in:
2026-02-08 11:14:14 -06:00
parent cac47724b1
commit 301ec6baeb
21 changed files with 1813 additions and 178 deletions

View File

@ -1,12 +1 @@
[
{
"timestamp": "2026-02-08T09:58:29.699634",
"action": "API Key Added",
"details": "Added key for OpenAI"
},
{
"timestamp": "2026-02-08T09:58:17.967014",
"action": "Budget Entry Added",
"details": "deposit of $100.00"
}
]
[]

View File

@ -1,10 +1 @@
[
{
"service": "OpenAI",
"name": "test-key",
"key": "sk-test123456789",
"created": "2026-02-08",
"expires": "2025-12-31",
"usage": 0
}
]
[]

View File

@ -1,9 +1 @@
[
{
"type": "deposit",
"service": "Test",
"amount": 100.0,
"description": "Initial test deposit",
"timestamp": "2026-02-08T09:58:17.966780"
}
]
[]

View File

@ -0,0 +1,17 @@
[
{
"title": "Set up DNS for .case remote access",
"description": "Configure DNS so feedhunter.case and admin.case resolve to 192.168.86.45 from all devices on the network.",
"category": "dns",
"priority": "medium",
"status": "pending",
"source": "Case",
"created": "2026-02-08 10:06",
"steps": [
"Option A: Add entries to your router's DNS settings (if supported)",
"Option B: Add to /etc/hosts on each device you want access from",
"Option C: Set up a local DNS server (Pi-hole, dnsmasq, etc.)",
"Entries needed: 192.168.86.45 feedhunter.case admin.case"
]
}
]

View File

@ -46,6 +46,8 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
self.serve_budget()
elif self.path == '/activity':
self.serve_activity()
elif self.path == '/todos':
self.serve_todos()
else:
self.send_error(404, "Not found")
@ -60,6 +62,8 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
self.handle_api_keys_post(form_data)
elif self.path == '/budget':
self.handle_budget_post(form_data)
elif self.path == '/todos':
self.handle_todos_post(form_data)
else:
self.send_error(404, "Not found")
@ -347,6 +351,7 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
<a href="/services">Services</a>
<a href="/budget">Budget</a>
<a href="/activity">Activity</a>
<a href="/todos">⚡ Action Required</a>
</nav>
</div>
</header>
@ -370,11 +375,13 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
accounts = self.load_data('accounts.json')
api_keys = self.load_data('api-keys.json')
budget = self.load_data('budget.json')
todos = self.load_data('todos.json')
# Calculate stats
total_accounts = len(accounts)
active_accounts = len([a for a in accounts if a.get('status') == 'active'])
total_api_keys = len(api_keys)
pending_todos = len([t for t in todos if t.get('status') == 'pending'])
monthly_spend = sum([b.get('amount', 0) for b in budget if
b.get('type') == 'spending' and
b.get('timestamp', '').startswith(datetime.now().strftime('%Y-%m'))])
@ -394,8 +401,8 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
<div class="stat-label">API Keys</div>
</div>
<div class="stat-card">
<span class="stat-number">${monthly_spend:.2f}</span>
<div class="stat-label">Monthly Spend</div>
<span class="stat-number" style="color:{'#f85149' if pending_todos > 0 else '#40c463'};">{pending_todos}</span>
<div class="stat-label">⚡ Actions Required</div>
</div>
</div>
@ -561,10 +568,10 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
def serve_services(self):
services = [
{"name": "Feed Hunter Portal", "port": 8888},
{"name": "Chrome Debug", "port": 9222},
{"name": "OpenClaw Gateway", "port": 18789},
{"name": "Case Control Panel", "port": 8000},
{"name": "Feed Hunter Portal", "port": 8888, "path": ""},
{"name": "Chrome Debug", "port": 9222, "path": ""},
{"name": "OpenClaw Gateway", "port": 18789, "path": ""},
{"name": "Case Control Panel", "port": 8000, "path": ""},
]
services_table = ""
@ -572,11 +579,13 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
is_healthy = self.check_service_health(service["port"])
status = "Running" if is_healthy else "Stopped"
status_class = "status-active" if is_healthy else "status-inactive"
url = f"http://localhost:{service['port']}{service['path']}"
link = f'<a href="{url}" target="_blank" style="color:#58a6ff;">{service["name"]}</a>'
services_table += f"""
<tr>
<td>{service['name']}</td>
<td>{service['port']}</td>
<td>{link}</td>
<td><a href="{url}" target="_blank" style="color:#c9d1d9;">{service['port']}</a></td>
<td><span class="{status_class}">{status}</span></td>
<td>N/A</td>
</tr>
@ -731,6 +740,96 @@ class ControlPanelHandler(BaseHTTPRequestHandler):
self.end_headers()
self.wfile.write(html.encode())
def serve_todos(self):
todos = self.load_data('todos.json')
pending = [t for t in todos if t.get('status') == 'pending']
done = [t for t in todos if t.get('status') == 'done']
priority_colors = {'high': '#f85149', 'medium': '#d29922', 'low': '#8b949e'}
category_icons = {'dns': '🌐', 'account': '🔑', 'config': '⚙️', 'install': '📦', 'other': '📋'}
pending_html = ""
for i, t in enumerate(pending):
pc = priority_colors.get(t.get('priority', 'medium'), '#d29922')
icon = category_icons.get(t.get('category', 'other'), '📋')
steps = ""
if t.get('steps'):
steps_list = "".join(f"<li>{s}</li>" for s in t['steps'])
steps = f'<div style="margin-top:8px;color:#8b949e;font-size:0.9em;"><strong>Steps:</strong><ol style="margin:4px 0 0 20px;">{steps_list}</ol></div>'
pending_html += f"""
<div class="card" style="border-left: 3px solid {pc};">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<span style="font-size:1.2em;">{icon}</span>
<strong style="color:#f0f6fc;">{t.get('title','Untitled')}</strong>
<span style="color:{pc};font-size:0.8em;margin-left:8px;">● {t.get('priority','medium').upper()}</span>
</div>
<form method="POST" style="display:inline;">
<input type="hidden" name="action" value="complete">
<input type="hidden" name="index" value="{i}">
<button type="submit" class="btn" style="background:#238636;">✓ Done</button>
</form>
</div>
<div style="color:#c9d1d9;margin-top:6px;">{t.get('description','')}</div>
{steps}
<div style="color:#484f58;font-size:0.8em;margin-top:8px;">Added {t.get('created','?')} by {t.get('source','unknown')}</div>
</div>"""
done_html = ""
for t in done[:10]:
done_html += f"""
<div style="padding:8px 12px;border-bottom:1px solid #21262d;color:#484f58;">
<span style="text-decoration:line-through;">{t.get('title','')}</span>
<span style="float:right;font-size:0.8em;">completed {t.get('completed','')}</span>
</div>"""
content = f"""
<div class="stats-grid">
<div class="stat-card">
<span class="stat-number" style="color:#f85149;">{len(pending)}</span>
<div class="stat-label">Pending Actions</div>
</div>
<div class="stat-card">
<span class="stat-number" style="color:#40c463;">{len(done)}</span>
<div class="stat-label">Completed</div>
</div>
</div>
<div class="card">
<div class="card-header" style="color:#f85149;">⚡ Action Required</div>
{pending_html if pending_html else '<p style="color:#484f58;">Nothing pending — all clear! 🎉</p>'}
</div>
<div class="card">
<div class="card-header">Recently Completed</div>
{done_html if done_html else '<p style="color:#484f58;">Nothing completed yet.</p>'}
</div>
"""
html = self.get_base_template("Action Required", content)
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(html.encode())
def handle_todos_post(self, form_data):
action = form_data.get('action', [''])[0]
todos = self.load_data('todos.json')
if action == 'complete':
idx = int(form_data.get('index', ['0'])[0])
pending = [t for t in todos if t.get('status') == 'pending']
if 0 <= idx < len(pending):
target = pending[idx]
target['status'] = 'done'
target['completed'] = datetime.now().strftime('%Y-%m-%d %H:%M')
self.save_data('todos.json', todos)
self.log_activity("Todo Completed", target.get('title', ''))
self.send_response(302)
self.send_header('Location', '/todos')
self.end_headers()
def handle_accounts_post(self, form_data):
if form_data.get('action', [''])[0] == 'add':
accounts = self.load_data('accounts.json')