183 lines
6.9 KiB
Python
183 lines
6.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Echo Task Board API — thin HTTP router.
|
|
|
|
All endpoint logic lives in `dashboard/handlers/*.py`. This file is
|
|
responsible only for URL dispatch, CORS, JSON response helpers, and
|
|
server bootstrap.
|
|
"""
|
|
import json
|
|
import sys
|
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
from pathlib import Path
|
|
|
|
# Make dashboard/ importable for the handler submodules (constants,
|
|
# habits_helpers, handlers.*). Tests rely on this as well.
|
|
_DASH = Path(__file__).parent
|
|
if str(_DASH) not in sys.path:
|
|
sys.path.insert(0, str(_DASH))
|
|
|
|
from constants import ( # noqa: E402 re-exported for tests
|
|
ALLOWED_WORKSPACES,
|
|
BASE_DIR,
|
|
ECHO_CORE_DIR,
|
|
ECHO_LOG_FILE,
|
|
ECHO_SESSIONS_FILE,
|
|
ECO_SERVICES,
|
|
GIT_WORKSPACE,
|
|
GITEA_ORG,
|
|
GITEA_TOKEN,
|
|
GITEA_URL,
|
|
HABITS_FILE,
|
|
KANBAN_DIR,
|
|
NOTES_DIR,
|
|
TOOLS_DIR,
|
|
VENV_PYTHON,
|
|
WORKSPACE_DIR,
|
|
)
|
|
from handlers.cron import CronHandlers # noqa: E402
|
|
from handlers.eco import EcoHandlers # noqa: E402
|
|
from handlers.files import FilesHandlers # noqa: E402
|
|
from handlers.git import GitHandlers # noqa: E402
|
|
from handlers.habits import HabitsHandlers # noqa: E402
|
|
from handlers.pdf import PDFHandlers # noqa: E402
|
|
from handlers.workspace import WorkspaceHandlers # noqa: E402
|
|
from handlers.youtube import YoutubeHandlers # noqa: E402
|
|
|
|
|
|
class TaskBoardHandler(
|
|
GitHandlers,
|
|
HabitsHandlers,
|
|
EcoHandlers,
|
|
FilesHandlers,
|
|
PDFHandlers,
|
|
YoutubeHandlers,
|
|
WorkspaceHandlers,
|
|
CronHandlers,
|
|
SimpleHTTPRequestHandler,
|
|
):
|
|
"""HTTP request handler — dispatches to handler-mixin methods."""
|
|
|
|
# ── shared utilities ────────────────────────────────────────
|
|
def _read_post_json(self):
|
|
"""Read a JSON body from the POST request."""
|
|
content_length = int(self.headers['Content-Length'])
|
|
post_data = self.rfile.read(content_length).decode('utf-8')
|
|
return json.loads(post_data)
|
|
|
|
def send_json(self, data, code=200):
|
|
self.send_response(code)
|
|
self.send_header('Content-Type', 'application/json')
|
|
self.send_header('Access-Control-Allow-Origin', '*')
|
|
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
self.send_header('Pragma', 'no-cache')
|
|
self.send_header('Expires', '0')
|
|
self.end_headers()
|
|
self.wfile.write(json.dumps(data).encode())
|
|
|
|
def do_OPTIONS(self):
|
|
self.send_response(200)
|
|
self.send_header('Access-Control-Allow-Origin', '*')
|
|
self.send_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
|
|
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
|
self.end_headers()
|
|
|
|
# ── dispatch ────────────────────────────────────────────────
|
|
def do_GET(self):
|
|
from datetime import datetime as _dt
|
|
if self.path == '/api/status':
|
|
self.send_json({'status': 'ok', 'time': _dt.now().isoformat()})
|
|
elif self.path == '/api/git' or self.path.startswith('/api/git?'):
|
|
self.handle_git_status()
|
|
elif self.path == '/api/cron' or self.path.startswith('/api/cron?'):
|
|
self.handle_cron_status()
|
|
elif self.path == '/api/habits':
|
|
self.handle_habits_get()
|
|
elif self.path.startswith('/api/files'):
|
|
self.handle_files_get()
|
|
elif self.path.startswith('/api/diff'):
|
|
self.handle_git_diff()
|
|
elif self.path == '/api/workspace' or self.path.startswith('/api/workspace?'):
|
|
self.handle_workspace_list()
|
|
elif self.path.startswith('/api/workspace/git/diff'):
|
|
self.handle_workspace_git_diff()
|
|
elif self.path.startswith('/api/workspace/logs'):
|
|
self.handle_workspace_logs()
|
|
elif self.path == '/api/eco/status' or self.path.startswith('/api/eco/status?'):
|
|
self.handle_eco_status()
|
|
elif self.path == '/api/eco/sessions' or self.path.startswith('/api/eco/sessions?'):
|
|
self.handle_eco_sessions()
|
|
elif self.path.startswith('/api/eco/sessions/content'):
|
|
self.handle_eco_session_content()
|
|
elif self.path.startswith('/api/eco/logs'):
|
|
self.handle_eco_logs()
|
|
elif self.path == '/api/eco/doctor':
|
|
self.handle_eco_doctor()
|
|
elif self.path == '/api/eco/git' or self.path.startswith('/api/eco/git?'):
|
|
self.handle_eco_git_status()
|
|
elif self.path.startswith('/api/'):
|
|
self.send_error(404)
|
|
else:
|
|
super().do_GET()
|
|
|
|
def do_POST(self):
|
|
if self.path == '/api/youtube':
|
|
self.handle_youtube()
|
|
elif self.path == '/api/files':
|
|
self.handle_files_post()
|
|
elif self.path == '/api/refresh-index':
|
|
self.handle_refresh_index()
|
|
elif self.path == '/api/pdf':
|
|
self.handle_pdf_post()
|
|
elif self.path == '/api/habits':
|
|
self.handle_habits_post()
|
|
elif self.path.startswith('/api/habits/') and self.path.endswith('/check'):
|
|
self.handle_habits_check()
|
|
elif self.path.startswith('/api/habits/') and self.path.endswith('/skip'):
|
|
self.handle_habits_skip()
|
|
elif self.path == '/api/workspace/run':
|
|
self.handle_workspace_run()
|
|
elif self.path == '/api/workspace/stop':
|
|
self.handle_workspace_stop()
|
|
elif self.path == '/api/workspace/git/commit':
|
|
self.handle_workspace_git_commit()
|
|
elif self.path == '/api/workspace/git/push':
|
|
self.handle_workspace_git_push()
|
|
elif self.path == '/api/workspace/delete':
|
|
self.handle_workspace_delete()
|
|
elif self.path == '/api/eco/restart':
|
|
self.handle_eco_restart()
|
|
elif self.path == '/api/eco/stop':
|
|
self.handle_eco_stop()
|
|
elif self.path == '/api/eco/sessions/clear':
|
|
self.handle_eco_sessions_clear()
|
|
elif self.path == '/api/eco/git-commit':
|
|
self.handle_eco_git_commit()
|
|
elif self.path == '/api/eco/restart-taskboard':
|
|
self.handle_eco_restart_taskboard()
|
|
else:
|
|
self.send_error(404)
|
|
|
|
def do_PUT(self):
|
|
if self.path.startswith('/api/habits/'):
|
|
self.handle_habits_put()
|
|
else:
|
|
self.send_error(404)
|
|
|
|
def do_DELETE(self):
|
|
if self.path.startswith('/api/habits/') and '/check' in self.path:
|
|
self.handle_habits_uncheck()
|
|
elif self.path.startswith('/api/habits/'):
|
|
self.handle_habits_delete()
|
|
else:
|
|
self.send_error(404)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import os
|
|
port = 8088
|
|
os.chdir(KANBAN_DIR)
|
|
|
|
print(f"Starting Echo Task Board API on port {port}")
|
|
httpd = HTTPServer(('0.0.0.0', port), TaskBoardHandler)
|
|
httpd.serve_forever()
|