kch123 analysis, copy-trade sim, monitoring, admin panel todos, nginx proxy
This commit is contained in:
@ -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')
|
||||
|
||||
Reference in New Issue
Block a user