mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-09-30 20:46:32 +00:00
fix: get the API booting (stale refactor imports + qdrant healthcheck)
The api crashed on import after a half-finished model refactor, and the qdrant
healthcheck used a binary not in the image.
- compose.yml: qdrant ships no wget/curl, so its healthcheck always failed and
blocked the api (depends_on service_healthy). Dropped the broken healthcheck and
gate the api on qdrant service_started (qdrant itself serves fine on :6333).
- db/__init__.py: reconcile to the real models — drop removed Node + Cluster
(now CRDs) and MPCWalletModel; export the actual set (User, OrgCluster,
SmartWallet, Subscription, TeamMember, etc.).
- api/mpc + api/keys: MPCWalletModel -> SmartWallet (the renamed model).
API now boots healthy: GET /health 200 {"status":"healthy","version":"2.0.0"},
/docs serving, 42 node/chain/fleet endpoints live (fleets deploy/scale, presets,
pod health/logs). Full local stack up: postgres, redis, clickhouse, qdrant,
meilisearch, api.
Note (follow-up, not blocking boot): api/nodes/db_nodes.py + db/seed_nodes.py
still import removed Node/Cluster models — dead code to remove or port to the
fleet/CRD path; the web frontend build fails on a stale pnpm lockfile.
Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -5,7 +5,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy import func as sa_func
|
||||
|
||||
from bootnode.api.deps import ApiKeyDep, DbDep, ProjectDep
|
||||
from bootnode.db.models import MPCWalletModel
|
||||
from bootnode.db.models import SmartWallet
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -22,18 +22,18 @@ async def get_key_status(
|
||||
Proxies to MPC wallet status data.
|
||||
"""
|
||||
result = await db.execute(
|
||||
select(sa_func.count(MPCWalletModel.id)).where(
|
||||
MPCWalletModel.project_id == project.id,
|
||||
MPCWalletModel.status == "active",
|
||||
select(sa_func.count(SmartWallet.id)).where(
|
||||
SmartWallet.project_id == project.id,
|
||||
SmartWallet.status == "active",
|
||||
)
|
||||
)
|
||||
total = result.scalar() or 0
|
||||
|
||||
# Get first active wallet as default
|
||||
first_result = await db.execute(
|
||||
select(MPCWalletModel).where(
|
||||
MPCWalletModel.project_id == project.id,
|
||||
MPCWalletModel.status == "active",
|
||||
select(SmartWallet).where(
|
||||
SmartWallet.project_id == project.id,
|
||||
SmartWallet.status == "active",
|
||||
).limit(1)
|
||||
)
|
||||
default_wallet = first_result.scalar_one_or_none()
|
||||
|
||||
@@ -18,7 +18,7 @@ from bootnode.core.mpc import (
|
||||
decrypt_share,
|
||||
encrypt_share,
|
||||
)
|
||||
from bootnode.db.models import MPCWalletModel
|
||||
from bootnode.db.models import SmartWallet
|
||||
|
||||
logger = structlog.get_logger()
|
||||
router = APIRouter()
|
||||
@@ -85,7 +85,7 @@ async def create_mpc_wallet(
|
||||
api_key: ApiKeyDep,
|
||||
project: ProjectDep,
|
||||
db: DbDep,
|
||||
) -> MPCWalletModel:
|
||||
) -> SmartWallet:
|
||||
"""Create a new MPC wallet with threshold key shares."""
|
||||
if request.threshold < 2:
|
||||
raise HTTPException(400, "Threshold must be at least 2")
|
||||
@@ -116,7 +116,7 @@ async def create_mpc_wallet(
|
||||
"3": "backup",
|
||||
}
|
||||
|
||||
wallet = MPCWalletModel(
|
||||
wallet = SmartWallet(
|
||||
project_id=project.id,
|
||||
address=keypair.address,
|
||||
chain=request.chain,
|
||||
@@ -148,13 +148,13 @@ async def list_mpc_wallets(
|
||||
project: ProjectDep,
|
||||
db: DbDep,
|
||||
chain: str | None = None,
|
||||
) -> list[MPCWalletModel]:
|
||||
) -> list[SmartWallet]:
|
||||
"""List all MPC wallets for the project."""
|
||||
query = select(MPCWalletModel).where(
|
||||
MPCWalletModel.project_id == project.id
|
||||
query = select(SmartWallet).where(
|
||||
SmartWallet.project_id == project.id
|
||||
)
|
||||
if chain:
|
||||
query = query.where(MPCWalletModel.chain == chain)
|
||||
query = query.where(SmartWallet.chain == chain)
|
||||
|
||||
result = await db.execute(query)
|
||||
return list(result.scalars().all())
|
||||
@@ -168,19 +168,19 @@ async def get_key_status(
|
||||
) -> MPCKeyStatusResponse:
|
||||
"""Get MPC wallet status for the project (used by staking page)."""
|
||||
result = await db.execute(
|
||||
select(sa_func.count(MPCWalletModel.id)).where(
|
||||
MPCWalletModel.project_id == project.id,
|
||||
MPCWalletModel.status == "active",
|
||||
select(sa_func.count(SmartWallet.id)).where(
|
||||
SmartWallet.project_id == project.id,
|
||||
SmartWallet.status == "active",
|
||||
)
|
||||
)
|
||||
total = result.scalar() or 0
|
||||
|
||||
# Get first wallet as default
|
||||
first = await db.execute(
|
||||
select(MPCWalletModel)
|
||||
select(SmartWallet)
|
||||
.where(
|
||||
MPCWalletModel.project_id == project.id,
|
||||
MPCWalletModel.status == "active",
|
||||
SmartWallet.project_id == project.id,
|
||||
SmartWallet.status == "active",
|
||||
)
|
||||
.limit(1)
|
||||
)
|
||||
@@ -207,12 +207,12 @@ async def get_mpc_wallet(
|
||||
api_key: ApiKeyDep,
|
||||
project: ProjectDep,
|
||||
db: DbDep,
|
||||
) -> MPCWalletModel:
|
||||
) -> SmartWallet:
|
||||
"""Get MPC wallet details."""
|
||||
result = await db.execute(
|
||||
select(MPCWalletModel).where(
|
||||
MPCWalletModel.address == address.lower(),
|
||||
MPCWalletModel.project_id == project.id,
|
||||
select(SmartWallet).where(
|
||||
SmartWallet.address == address.lower(),
|
||||
SmartWallet.project_id == project.id,
|
||||
)
|
||||
)
|
||||
wallet = result.scalar_one_or_none()
|
||||
@@ -231,9 +231,9 @@ async def sign_with_mpc(
|
||||
) -> MPCSignResponse:
|
||||
"""Sign a message or transaction using threshold shares."""
|
||||
result = await db.execute(
|
||||
select(MPCWalletModel).where(
|
||||
MPCWalletModel.address == address.lower(),
|
||||
MPCWalletModel.project_id == project.id,
|
||||
select(SmartWallet).where(
|
||||
SmartWallet.address == address.lower(),
|
||||
SmartWallet.project_id == project.id,
|
||||
)
|
||||
)
|
||||
db_wallet = result.scalar_one_or_none()
|
||||
@@ -317,9 +317,9 @@ async def get_share_info(
|
||||
) -> dict:
|
||||
"""Get share holder info (not the actual shares)."""
|
||||
result = await db.execute(
|
||||
select(MPCWalletModel).where(
|
||||
MPCWalletModel.address == address.lower(),
|
||||
MPCWalletModel.project_id == project.id,
|
||||
select(SmartWallet).where(
|
||||
SmartWallet.address == address.lower(),
|
||||
SmartWallet.project_id == project.id,
|
||||
)
|
||||
)
|
||||
wallet = result.scalar_one_or_none()
|
||||
@@ -345,9 +345,9 @@ async def decrypt_wallet_share(
|
||||
) -> dict:
|
||||
"""Decrypt a specific share for signing (requires auth)."""
|
||||
result = await db.execute(
|
||||
select(MPCWalletModel).where(
|
||||
MPCWalletModel.address == address.lower(),
|
||||
MPCWalletModel.project_id == project.id,
|
||||
select(SmartWallet).where(
|
||||
SmartWallet.address == address.lower(),
|
||||
SmartWallet.project_id == project.id,
|
||||
)
|
||||
)
|
||||
wallet = result.scalar_one_or_none()
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
from bootnode.db.models import (
|
||||
ApiKey,
|
||||
Base,
|
||||
Cluster,
|
||||
MPCWalletModel,
|
||||
Node,
|
||||
GasPolicy,
|
||||
OrgCluster,
|
||||
Project,
|
||||
SmartWallet,
|
||||
Subscription,
|
||||
TeamMember,
|
||||
Usage,
|
||||
User,
|
||||
Webhook,
|
||||
WebhookDelivery,
|
||||
)
|
||||
from bootnode.db.session import async_session, engine, get_db, init_db
|
||||
|
||||
@@ -18,11 +22,15 @@ __all__ = [
|
||||
"async_session",
|
||||
"init_db",
|
||||
"Base",
|
||||
"Cluster",
|
||||
"Node",
|
||||
"Project",
|
||||
"ApiKey",
|
||||
"MPCWalletModel",
|
||||
"Webhook",
|
||||
"GasPolicy",
|
||||
"OrgCluster",
|
||||
"Project",
|
||||
"SmartWallet",
|
||||
"Subscription",
|
||||
"TeamMember",
|
||||
"Usage",
|
||||
"User",
|
||||
"Webhook",
|
||||
"WebhookDelivery",
|
||||
]
|
||||
|
||||
+3
-6
@@ -79,11 +79,8 @@ services:
|
||||
environment:
|
||||
QDRANT__SERVICE__GRPC_PORT: 6334
|
||||
QDRANT__LOG_LEVEL: INFO
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:6333/"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
# qdrant image ships no wget/curl; readiness is gated via depends_on:
|
||||
# service_started + the api's own startup checks instead of a broken healthcheck.
|
||||
|
||||
# Meilisearch - Full-text search for addresses, tokens, contracts
|
||||
meilisearch:
|
||||
@@ -151,7 +148,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
qdrant:
|
||||
condition: service_healthy
|
||||
condition: service_started
|
||||
meilisearch:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
|
||||
Reference in New Issue
Block a user