|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
|
|
|
|
|
from contextlib import asynccontextmanager
|
|
from contextlib import asynccontextmanager
|
|
|
from datetime import datetime, timezone
|
|
from datetime import datetime, timezone
|
|
|
|
|
+from sqlite3 import IntegrityError
|
|
|
from uuid import uuid4
|
|
from uuid import uuid4
|
|
|
|
|
|
|
|
from fastapi import FastAPI, Form, HTTPException
|
|
from fastapi import FastAPI, Form, HTTPException
|
|
@@ -27,9 +28,10 @@ SUPPORTED_VENUES = {"bitstamp"}
|
|
|
|
|
|
|
|
|
|
|
|
|
class AccountView(BaseModel):
|
|
class AccountView(BaseModel):
|
|
|
- display_name: str
|
|
|
|
|
- venue: str
|
|
|
|
|
- venue_account_ref: str
|
|
|
|
|
|
|
+ id: str
|
|
|
|
|
+ display_name: str | None = None
|
|
|
|
|
+ venue: str | None = None
|
|
|
|
|
+ venue_account_ref: str | None = None
|
|
|
description: str | None = None
|
|
description: str | None = None
|
|
|
enabled: bool
|
|
enabled: bool
|
|
|
metadata: str = Field(default="{}")
|
|
metadata: str = Field(default="{}")
|
|
@@ -70,16 +72,16 @@ def http_dashboard() -> str:
|
|
|
table_rows = "".join(
|
|
table_rows = "".join(
|
|
|
f"""
|
|
f"""
|
|
|
<tr>
|
|
<tr>
|
|
|
- <td>{row['display_name']}</td>
|
|
|
|
|
- <td>{row['venue']}</td>
|
|
|
|
|
- <td>{row['venue_account_ref']}</td>
|
|
|
|
|
|
|
+ <td>{row['display_name'] or ''}</td>
|
|
|
|
|
+ <td>{row['venue'] or ''}</td>
|
|
|
|
|
+ <td>{row['venue_account_ref'] or ''}</td>
|
|
|
<td>{row['description'] or ''}</td>
|
|
<td>{row['description'] or ''}</td>
|
|
|
<td>{'yes' if row['enabled'] else 'no'}</td>
|
|
<td>{'yes' if row['enabled'] else 'no'}</td>
|
|
|
<td class="actions">
|
|
<td class="actions">
|
|
|
- <form method="get" action="/dashboard/accounts/{row['venue']}/{row['venue_account_ref']}/edit">
|
|
|
|
|
|
|
+ <form method="get" action="/dashboard/accounts/{row['id']}/edit">
|
|
|
<button type="submit">Edit</button>
|
|
<button type="submit">Edit</button>
|
|
|
</form>
|
|
</form>
|
|
|
- <form method="post" action="/dashboard/accounts/{row['venue']}/{row['venue_account_ref']}/delete">
|
|
|
|
|
|
|
+ <form method="post" action="/dashboard/accounts/{row['id']}/delete">
|
|
|
<button type="submit" class="danger">Delete</button>
|
|
<button type="submit" class="danger">Delete</button>
|
|
|
</form>
|
|
</form>
|
|
|
</td>
|
|
</td>
|
|
@@ -96,6 +98,8 @@ def http_dashboard() -> str:
|
|
|
header {{ border-bottom: 1px solid #e5e7eb; margin-bottom: 20px; }}
|
|
header {{ border-bottom: 1px solid #e5e7eb; margin-bottom: 20px; }}
|
|
|
footer {{ border-top: 1px solid #e5e7eb; margin-top: 32px; color: #6b7280; }}
|
|
footer {{ border-top: 1px solid #e5e7eb; margin-top: 32px; color: #6b7280; }}
|
|
|
.panel {{ background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; margin-bottom: 20px; }}
|
|
.panel {{ background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; margin-bottom: 20px; }}
|
|
|
|
|
+ summary {{ list-style: none; }}
|
|
|
|
|
+ summary::-webkit-details-marker {{ display: none; }}
|
|
|
form {{ display: grid; gap: 10px; max-width: 520px; }}
|
|
form {{ display: grid; gap: 10px; max-width: 520px; }}
|
|
|
input, select, button {{ padding: 10px 12px; border-radius: 8px; border: 1px solid #cbd5e1; }}
|
|
input, select, button {{ padding: 10px 12px; border-radius: 8px; border: 1px solid #cbd5e1; }}
|
|
|
button {{ background: #111827; color: white; cursor: pointer; }}
|
|
button {{ background: #111827; color: white; cursor: pointer; }}
|
|
@@ -104,7 +108,6 @@ def http_dashboard() -> str:
|
|
|
th, td {{ border-bottom: 1px solid #e5e7eb; padding: 10px 8px; text-align: left; vertical-align: top; }}
|
|
th, td {{ border-bottom: 1px solid #e5e7eb; padding: 10px 8px; text-align: left; vertical-align: top; }}
|
|
|
.actions {{ display: flex; gap: 8px; align-items: center; }}
|
|
.actions {{ display: flex; gap: 8px; align-items: center; }}
|
|
|
.actions form {{ display: inline; }}
|
|
.actions form {{ display: inline; }}
|
|
|
- .actions a {{ color: #1d4ed8; text-decoration: none; }}
|
|
|
|
|
</style>
|
|
</style>
|
|
|
</head>
|
|
</head>
|
|
|
<body>
|
|
<body>
|
|
@@ -112,19 +115,19 @@ def http_dashboard() -> str:
|
|
|
<h1>exec-mcp dashboard</h1>
|
|
<h1>exec-mcp dashboard</h1>
|
|
|
</header>
|
|
</header>
|
|
|
|
|
|
|
|
- <div class="panel">
|
|
|
|
|
- <h2>Create account</h2>
|
|
|
|
|
- <form method="post" action="/dashboard/accounts/create">
|
|
|
|
|
- <input name="display_name" placeholder="name" required />
|
|
|
|
|
|
|
+ <details class="panel">
|
|
|
|
|
+ <summary style="cursor:pointer; font-size: 1.1rem; font-weight: 600;">Create account</summary>
|
|
|
|
|
+ <form method="post" action="/dashboard/accounts/create" style="margin-top: 12px;">
|
|
|
|
|
+ <input name="display_name" placeholder="name" />
|
|
|
<select name="venue">{options}</select>
|
|
<select name="venue">{options}</select>
|
|
|
- <input name="venue_account_ref" placeholder="exchange account ref" required />
|
|
|
|
|
|
|
+ <input name="venue_account_ref" placeholder="exchange account ref" />
|
|
|
<input name="api_key" placeholder="api key" required />
|
|
<input name="api_key" placeholder="api key" required />
|
|
|
<input name="api_secret" placeholder="api secret" required />
|
|
<input name="api_secret" placeholder="api secret" required />
|
|
|
<input name="description" placeholder="description" />
|
|
<input name="description" placeholder="description" />
|
|
|
<label><input type="checkbox" name="enabled" checked /> enabled</label>
|
|
<label><input type="checkbox" name="enabled" checked /> enabled</label>
|
|
|
<button type="submit">Create</button>
|
|
<button type="submit">Create</button>
|
|
|
</form>
|
|
</form>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </details>
|
|
|
|
|
|
|
|
<div class="panel">
|
|
<div class="panel">
|
|
|
<h2>Accounts</h2>
|
|
<h2>Accounts</h2>
|
|
@@ -146,9 +149,9 @@ def http_dashboard() -> str:
|
|
|
|
|
|
|
|
@app.post("/dashboard/accounts/create")
|
|
@app.post("/dashboard/accounts/create")
|
|
|
def http_dashboard_create_account(
|
|
def http_dashboard_create_account(
|
|
|
- display_name: str = Form(...),
|
|
|
|
|
|
|
+ display_name: str = Form(""),
|
|
|
venue: str = Form(...),
|
|
venue: str = Form(...),
|
|
|
- venue_account_ref: str = Form(...),
|
|
|
|
|
|
|
+ venue_account_ref: str = Form(""),
|
|
|
api_key: str = Form(...),
|
|
api_key: str = Form(...),
|
|
|
api_secret: str = Form(...),
|
|
api_secret: str = Form(...),
|
|
|
description: str | None = Form(None),
|
|
description: str | None = Form(None),
|
|
@@ -166,12 +169,12 @@ def http_dashboard_create_account(
|
|
|
return RedirectResponse(url="/dashboard", status_code=303)
|
|
return RedirectResponse(url="/dashboard", status_code=303)
|
|
|
|
|
|
|
|
|
|
|
|
|
-@app.get("/dashboard/accounts/{venue}/{venue_account_ref}/edit", response_class=HTMLResponse)
|
|
|
|
|
-def http_dashboard_edit_account(venue: str, venue_account_ref: str) -> str:
|
|
|
|
|
|
|
+@app.get("/dashboard/accounts/{account_id}/edit", response_class=HTMLResponse)
|
|
|
|
|
+def http_dashboard_edit_account(account_id: str) -> str:
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
row = conn.execute(
|
|
row = conn.execute(
|
|
|
- "SELECT display_name, venue, venue_account_ref, description, enabled FROM accounts WHERE venue = ? AND venue_account_ref = ?",
|
|
|
|
|
- (venue, venue_account_ref),
|
|
|
|
|
|
|
+ "SELECT id, display_name, venue, venue_account_ref, description, enabled FROM accounts WHERE id = ?",
|
|
|
|
|
+ (account_id,),
|
|
|
).fetchone()
|
|
).fetchone()
|
|
|
if row is None:
|
|
if row is None:
|
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
@@ -179,52 +182,54 @@ def http_dashboard_edit_account(venue: str, venue_account_ref: str) -> str:
|
|
|
return f"""
|
|
return f"""
|
|
|
<html>
|
|
<html>
|
|
|
<body style="font-family: system-ui, sans-serif; max-width: 700px; margin: 32px auto; padding: 0 16px;">
|
|
<body style="font-family: system-ui, sans-serif; max-width: 700px; margin: 32px auto; padding: 0 16px;">
|
|
|
- <h1>Edit account</h1>
|
|
|
|
|
|
|
+ <header style="margin-bottom: 24px; border-bottom: 1px solid #e5e7eb; padding-bottom: 12px;"><h1>Edit account</h1></header>
|
|
|
<p><a href="/dashboard">Back to dashboard</a></p>
|
|
<p><a href="/dashboard">Back to dashboard</a></p>
|
|
|
- <form method="post" action="/dashboard/accounts/{venue}/{venue_account_ref}/update" style="display:grid; gap:10px; max-width: 520px;">
|
|
|
|
|
- <input name="display_name" value="{row['display_name']}" placeholder="name" required />
|
|
|
|
|
- <input value="{row['venue_account_ref']}" placeholder="exchange account ref" readonly />
|
|
|
|
|
|
|
+ <form method="post" action="/dashboard/accounts/{row['id']}/update" style="display:grid; gap:10px; max-width: 520px;">
|
|
|
|
|
+ <input name="display_name" value="{row['display_name'] or ''}" placeholder="name" />
|
|
|
|
|
+ <input value="{row['venue'] or ''}" placeholder="venue" readonly />
|
|
|
|
|
+ <input value="{row['venue_account_ref'] or ''}" placeholder="exchange account ref" readonly />
|
|
|
<input name="description" value="{row['description'] or ''}" placeholder="description" />
|
|
<input name="description" value="{row['description'] or ''}" placeholder="description" />
|
|
|
<label><input type="checkbox" name="enabled" {checked} /> enabled</label>
|
|
<label><input type="checkbox" name="enabled" {checked} /> enabled</label>
|
|
|
<button type="submit">Save</button>
|
|
<button type="submit">Save</button>
|
|
|
</form>
|
|
</form>
|
|
|
|
|
+ <footer style="margin-top: 32px; padding-top: 12px; border-top: 1px solid #e5e7eb; color: #6b7280;">exec-mcp</footer>
|
|
|
</body>
|
|
</body>
|
|
|
</html>
|
|
</html>
|
|
|
"""
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
-@app.post("/dashboard/accounts/{venue}/{venue_account_ref}/update")
|
|
|
|
|
|
|
+@app.post("/dashboard/accounts/{account_id}/update")
|
|
|
def http_dashboard_update_account(
|
|
def http_dashboard_update_account(
|
|
|
- venue: str,
|
|
|
|
|
- venue_account_ref: str,
|
|
|
|
|
- display_name: str = Form(...),
|
|
|
|
|
|
|
+ account_id: str,
|
|
|
|
|
+ display_name: str = Form(""),
|
|
|
description: str = Form(""),
|
|
description: str = Form(""),
|
|
|
enabled: bool = Form(False),
|
|
enabled: bool = Form(False),
|
|
|
) -> RedirectResponse:
|
|
) -> RedirectResponse:
|
|
|
- update_account(venue=venue, venue_account_ref=venue_account_ref, display_name=display_name, description=description or None, enabled=enabled)
|
|
|
|
|
|
|
+ update_account(account_id=account_id, display_name=display_name, description=description or None, enabled=enabled)
|
|
|
return RedirectResponse(url="/dashboard", status_code=303)
|
|
return RedirectResponse(url="/dashboard", status_code=303)
|
|
|
|
|
|
|
|
|
|
|
|
|
-@app.post("/dashboard/accounts/{venue}/{venue_account_ref}/delete")
|
|
|
|
|
-def http_dashboard_delete_account(venue: str, venue_account_ref: str) -> RedirectResponse:
|
|
|
|
|
- delete_account(venue=venue, venue_account_ref=venue_account_ref)
|
|
|
|
|
|
|
+@app.post("/dashboard/accounts/{account_id}/delete")
|
|
|
|
|
+def http_dashboard_delete_account(account_id: str) -> RedirectResponse:
|
|
|
|
|
+ delete_account(account_id=account_id)
|
|
|
return RedirectResponse(url="/dashboard", status_code=303)
|
|
return RedirectResponse(url="/dashboard", status_code=303)
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
|
def list_accounts(venue: str | None = None) -> list[dict]:
|
|
def list_accounts(venue: str | None = None) -> list[dict]:
|
|
|
- query = "SELECT display_name, venue, venue_account_ref, description, enabled, metadata_json, created_at, updated_at FROM accounts"
|
|
|
|
|
|
|
+ query = "SELECT id, display_name, venue, venue_account_ref, description, enabled, metadata_json, created_at, updated_at FROM accounts"
|
|
|
params: tuple = ()
|
|
params: tuple = ()
|
|
|
if venue:
|
|
if venue:
|
|
|
query += " WHERE venue = ?"
|
|
query += " WHERE venue = ?"
|
|
|
params = (venue,)
|
|
params = (venue,)
|
|
|
- query += " ORDER BY display_name ASC"
|
|
|
|
|
|
|
+ query += " ORDER BY created_at ASC"
|
|
|
|
|
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
rows = conn.execute(query, params).fetchall()
|
|
rows = conn.execute(query, params).fetchall()
|
|
|
|
|
|
|
|
return [
|
|
return [
|
|
|
{
|
|
{
|
|
|
|
|
+ "id": row["id"],
|
|
|
"display_name": row["display_name"],
|
|
"display_name": row["display_name"],
|
|
|
"venue": row["venue"],
|
|
"venue": row["venue"],
|
|
|
"venue_account_ref": row["venue_account_ref"],
|
|
"venue_account_ref": row["venue_account_ref"],
|
|
@@ -239,11 +244,11 @@ def list_accounts(venue: str | None = None) -> list[dict]:
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
|
-def get_account_info(venue: str, venue_account_ref: str) -> dict:
|
|
|
|
|
|
|
+def get_account_info(account_id: str) -> dict:
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
account = conn.execute(
|
|
account = conn.execute(
|
|
|
- "SELECT id, display_name, venue, venue_account_ref, description, enabled, metadata_json FROM accounts WHERE venue = ? AND venue_account_ref = ?",
|
|
|
|
|
- (venue, venue_account_ref),
|
|
|
|
|
|
|
+ "SELECT id, display_name, venue, venue_account_ref, description, enabled, metadata_json FROM accounts WHERE id = ?",
|
|
|
|
|
+ (account_id,),
|
|
|
).fetchone()
|
|
).fetchone()
|
|
|
secrets = None
|
|
secrets = None
|
|
|
if account is not None:
|
|
if account is not None:
|
|
@@ -255,8 +260,8 @@ def get_account_info(venue: str, venue_account_ref: str) -> dict:
|
|
|
if account is None:
|
|
if account is None:
|
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
|
|
|
|
|
|
- # Do not expose the hidden internal primary key or raw secret material.
|
|
|
|
|
return {
|
|
return {
|
|
|
|
|
+ "id": account["id"],
|
|
|
"display_name": account["display_name"],
|
|
"display_name": account["display_name"],
|
|
|
"venue": account["venue"],
|
|
"venue": account["venue"],
|
|
|
"venue_account_ref": account["venue_account_ref"],
|
|
"venue_account_ref": account["venue_account_ref"],
|
|
@@ -283,97 +288,66 @@ def create_account(
|
|
|
account_pk = str(uuid4())
|
|
account_pk = str(uuid4())
|
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
- conn.execute(
|
|
|
|
|
- """
|
|
|
|
|
- INSERT INTO accounts (id, display_name, venue, venue_account_ref, description, enabled, created_at, updated_at)
|
|
|
|
|
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
|
|
- ON CONFLICT(venue, venue_account_ref) DO UPDATE SET
|
|
|
|
|
- display_name=excluded.display_name,
|
|
|
|
|
- description=excluded.description,
|
|
|
|
|
- enabled=excluded.enabled,
|
|
|
|
|
- updated_at=excluded.updated_at
|
|
|
|
|
- """,
|
|
|
|
|
- (account_pk, display_name, venue, venue_account_ref, description, int(enabled), now, now),
|
|
|
|
|
- )
|
|
|
|
|
- account = conn.execute(
|
|
|
|
|
- "SELECT id FROM accounts WHERE venue = ? AND venue_account_ref = ?",
|
|
|
|
|
- (venue, venue_account_ref),
|
|
|
|
|
- ).fetchone()
|
|
|
|
|
- conn.execute(
|
|
|
|
|
- """
|
|
|
|
|
- INSERT INTO account_secrets (account_id, api_key, api_secret, created_at, updated_at)
|
|
|
|
|
- VALUES (?, ?, ?, ?, ?)
|
|
|
|
|
- ON CONFLICT(account_id) DO UPDATE SET
|
|
|
|
|
- api_key=excluded.api_key,
|
|
|
|
|
- api_secret=excluded.api_secret,
|
|
|
|
|
- updated_at=excluded.updated_at
|
|
|
|
|
- """,
|
|
|
|
|
- (account["id"], api_key, api_secret, now, now),
|
|
|
|
|
- )
|
|
|
|
|
- conn.commit()
|
|
|
|
|
|
|
+ try:
|
|
|
|
|
+ conn.execute(
|
|
|
|
|
+ """
|
|
|
|
|
+ INSERT INTO accounts (id, display_name, venue, venue_account_ref, description, enabled, created_at, updated_at)
|
|
|
|
|
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
|
|
|
+ """,
|
|
|
|
|
+ (account_pk, display_name, venue, venue_account_ref, description, int(enabled), now, now),
|
|
|
|
|
+ )
|
|
|
|
|
+ conn.execute(
|
|
|
|
|
+ """
|
|
|
|
|
+ INSERT INTO account_secrets (account_id, api_key, api_secret, created_at, updated_at)
|
|
|
|
|
+ VALUES (?, ?, ?, ?, ?)
|
|
|
|
|
+ """,
|
|
|
|
|
+ (account_pk, api_key, api_secret, now, now),
|
|
|
|
|
+ )
|
|
|
|
|
+ conn.commit()
|
|
|
|
|
+ except IntegrityError as exc:
|
|
|
|
|
+ conn.rollback()
|
|
|
|
|
+ if "api_key" in str(exc).lower():
|
|
|
|
|
+ raise HTTPException(status_code=409, detail="api key already exists") from exc
|
|
|
|
|
+ raise
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
- "display_name": display_name,
|
|
|
|
|
- "venue": venue,
|
|
|
|
|
- "venue_account_ref": venue_account_ref,
|
|
|
|
|
- "enabled": enabled,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return {"id": account_pk, "display_name": display_name, "venue": venue}
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_account(
|
|
def update_account(
|
|
|
- venue: str,
|
|
|
|
|
- venue_account_ref: str,
|
|
|
|
|
|
|
+ account_id: str,
|
|
|
display_name: str | None = None,
|
|
display_name: str | None = None,
|
|
|
description: str | None = None,
|
|
description: str | None = None,
|
|
|
enabled: bool | None = None,
|
|
enabled: bool | None = None,
|
|
|
) -> dict:
|
|
) -> dict:
|
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
- row = conn.execute(
|
|
|
|
|
- "SELECT id FROM accounts WHERE venue = ? AND venue_account_ref = ?",
|
|
|
|
|
- (venue, venue_account_ref),
|
|
|
|
|
- ).fetchone()
|
|
|
|
|
|
|
+ row = conn.execute("SELECT id FROM accounts WHERE id = ?", (account_id,)).fetchone()
|
|
|
if row is None:
|
|
if row is None:
|
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
|
if display_name is not None:
|
|
if display_name is not None:
|
|
|
- conn.execute(
|
|
|
|
|
- "UPDATE accounts SET display_name = ?, updated_at = ? WHERE id = ?",
|
|
|
|
|
- (display_name, now, row["id"]),
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ conn.execute("UPDATE accounts SET display_name = ?, updated_at = ? WHERE id = ?", (display_name, now, account_id))
|
|
|
if description is not None:
|
|
if description is not None:
|
|
|
- conn.execute(
|
|
|
|
|
- "UPDATE accounts SET description = ?, updated_at = ? WHERE id = ?",
|
|
|
|
|
- (description, now, row["id"]),
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ conn.execute("UPDATE accounts SET description = ?, updated_at = ? WHERE id = ?", (description, now, account_id))
|
|
|
if enabled is not None:
|
|
if enabled is not None:
|
|
|
- conn.execute(
|
|
|
|
|
- "UPDATE accounts SET enabled = ?, updated_at = ? WHERE id = ?",
|
|
|
|
|
- (int(enabled), now, row["id"]),
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ conn.execute("UPDATE accounts SET enabled = ?, updated_at = ? WHERE id = ?", (int(enabled), now, account_id))
|
|
|
conn.commit()
|
|
conn.commit()
|
|
|
- return {"venue": venue, "venue_account_ref": venue_account_ref, "updated": True}
|
|
|
|
|
|
|
+ return {"id": account_id, "updated": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
-def delete_account(venue: str, venue_account_ref: str) -> dict:
|
|
|
|
|
|
|
+def delete_account(account_id: str) -> dict:
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
- deleted = conn.execute(
|
|
|
|
|
- "DELETE FROM accounts WHERE venue = ? AND venue_account_ref = ?",
|
|
|
|
|
- (venue, venue_account_ref),
|
|
|
|
|
- ).rowcount
|
|
|
|
|
|
|
+ deleted = conn.execute("DELETE FROM accounts WHERE id = ?", (account_id,)).rowcount
|
|
|
conn.commit()
|
|
conn.commit()
|
|
|
if not deleted:
|
|
if not deleted:
|
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
|
- return {"venue": venue, "venue_account_ref": venue_account_ref, "deleted": True}
|
|
|
|
|
|
|
+ return {"id": account_id, "deleted": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
-def record_balance(venue: str, venue_account_ref: str, asset_code: str, balance_value: float) -> dict:
|
|
|
|
|
|
|
+def record_balance(account_id: str, asset_code: str, balance_value: float) -> dict:
|
|
|
# Internal helper, called by execution sync logic when balances change.
|
|
# Internal helper, called by execution sync logic when balances change.
|
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
now = datetime.now(timezone.utc).isoformat()
|
|
|
with get_connection() as conn:
|
|
with get_connection() as conn:
|
|
|
- account = conn.execute(
|
|
|
|
|
- "SELECT id FROM accounts WHERE venue = ? AND venue_account_ref = ?",
|
|
|
|
|
- (venue, venue_account_ref),
|
|
|
|
|
- ).fetchone()
|
|
|
|
|
|
|
+ account = conn.execute("SELECT id FROM accounts WHERE id = ?", (account_id,)).fetchone()
|
|
|
if account is None:
|
|
if account is None:
|
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
raise HTTPException(status_code=404, detail="account not found")
|
|
|
conn.execute(
|
|
conn.execute(
|
|
@@ -381,13 +355,7 @@ def record_balance(venue: str, venue_account_ref: str, asset_code: str, balance_
|
|
|
(account["id"], asset_code, balance_value, now),
|
|
(account["id"], asset_code, balance_value, now),
|
|
|
)
|
|
)
|
|
|
conn.commit()
|
|
conn.commit()
|
|
|
- return {
|
|
|
|
|
- "venue": venue,
|
|
|
|
|
- "venue_account_ref": venue_account_ref,
|
|
|
|
|
- "asset_code": asset_code,
|
|
|
|
|
- "balance_value": balance_value,
|
|
|
|
|
- "captured_at": now,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return {"id": account_id, "asset_code": asset_code, "balance_value": balance_value, "captured_at": now}
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
def main() -> None:
|