diff --git a/apps/aitbc-edge/edge-api.service b/apps/aitbc-edge/edge-api.service index 25470f99..b8e773a2 100644 --- a/apps/aitbc-edge/edge-api.service +++ b/apps/aitbc-edge/edge-api.service @@ -4,17 +4,18 @@ After=network.target postgresql.service [Service] Type=simple -WorkingDirectory=/opt/aitbc/apps/edge-api +WorkingDirectory=/opt/aitbc/apps/aitbc-edge Environment="PATH=/opt/aitbc/venv/bin" -Environment="PYTHONPATH=/opt/aitbc/packages/py/aitbc-core/src:/opt/aitbc/apps/edge-api/src:/opt/aitbc" -Environment="DATABASE_URL=postgresql+asyncpg://aitbc_edge:password@localhost:5432/aitbc_edge" +Environment="PYTHONPATH=/opt/aitbc/packages/py/aitbc-core/src:/opt/aitbc/apps/aitbc-edge/src:/opt/aitbc" +Environment="DATABASE_URL=sqlite+aiosqlite:///opt/aitbc/data/edge.db" +Environment="DATA_DIR=/opt/aitbc/data" Environment="BLOCKCHAIN_RPC_HOST=localhost" Environment="BLOCKCHAIN_RPC_PORT=8006" Environment="GPU_SERVICE_HOST=localhost" Environment="GPU_SERVICE_PORT=8101" Environment="JWT_SECRET_KEY=CQNLjrtnUVGzdO1skuLsxoiPEEmav2Vj3aA302cvo8I" Environment="API_PORT=8103" -ExecStart=/opt/aitbc/venv/bin/python -m edge_api.main +ExecStart=/opt/aitbc/venv/bin/python -m aitbc_edge.main Restart=always RestartSec=10 diff --git a/apps/aitbc-edge/src/aitbc_edge/schemas/island.py b/apps/aitbc-edge/src/aitbc_edge/schemas/island.py index f3f08b08..351b14df 100644 --- a/apps/aitbc-edge/src/aitbc_edge/schemas/island.py +++ b/apps/aitbc-edge/src/aitbc_edge/schemas/island.py @@ -1,10 +1,10 @@ """Island-related schemas for Edge API Service""" -from datetime import datetime +from datetime import datetime, timezone from enum import StrEnum from uuid import uuid4 -from sqlalchemy import JSON, Column, Enum as SQLEnum +from sqlalchemy import JSON, Column, Enum as SQLEnum, String from sqlmodel import Field, SQLModel @@ -18,37 +18,37 @@ class IslandStatus(StrEnum): class IslandMembership(SQLModel, table=True): """Island membership in edge API database""" - + __tablename__ = "island_memberships" __table_args__ = {"extend_existing": True} - + id: str = Field(default_factory=lambda: f"membership_{uuid4().hex[:8]}", primary_key=True) - island_id: str = Field(sa_column=Column(index=True)) - island_name: str - chain_id: str = Field(sa_column=Column(index=True)) + island_id: str = Field(sa_column=Column(String, index=True)) + island_name: str = Field(sa_column=Column(String)) + chain_id: str = Field(sa_column=Column(String, index=True)) status: IslandStatus = Field( default=IslandStatus.ACTIVE, sa_column=Column(SQLEnum(IslandStatus, values_only=True), index=True) ) - role: str = Field(default="compute-provider") # compute-provider, consumer, hub - joined_at: datetime = Field(default_factory=lambda: datetime.utcnow()) + role: str = Field(default="compute-provider", sa_column=Column(String)) # compute-provider, consumer, hub + joined_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) peer_count: int = Field(default=0) - + # Additional metadata extra_data: dict = Field(default_factory=dict, sa_column=Column(JSON, nullable=True)) class BridgeRequest(SQLModel, table=True): """Bridge request for island connectivity""" - + __tablename__ = "bridge_requests" __table_args__ = {"extend_existing": True} - + id: str = Field(default_factory=lambda: f"bridge_req_{uuid4().hex[:8]}", primary_key=True) - request_id: str = Field(index=True) - source_island_id: str - target_island_id: str - source_node_id: str - status: str = Field(default="pending", index=True) # pending, approved, rejected - created_at: datetime = Field(default_factory=lambda: datetime.utcnow()) - updated_at: datetime = Field(default_factory=lambda: datetime.utcnow()) + request_id: str = Field(sa_column=Column(String, index=True)) + source_island_id: str = Field(sa_column=Column(String)) + target_island_id: str = Field(sa_column=Column(String)) + source_node_id: str = Field(sa_column=Column(String)) + status: str = Field(default="pending", sa_column=Column(String, index=True)) # pending, approved, rejected + created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) + updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) diff --git a/apps/aitbc-edge/src/aitbc_edge/storage.py b/apps/aitbc-edge/src/aitbc_edge/storage.py index 0827f501..a0d3bc83 100644 --- a/apps/aitbc-edge/src/aitbc_edge/storage.py +++ b/apps/aitbc-edge/src/aitbc_edge/storage.py @@ -12,7 +12,15 @@ from aitbc import get_logger logger = get_logger(__name__) # Database URL from environment variable or default -DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://aitbc_edge:password@localhost:5432/aitbc_edge") +DATABASE_URL = os.getenv("DATABASE_URL", "sqlite+aiosqlite:///opt/aitbc/data/edge.db") + +# Ensure data directory exists +if DATABASE_URL.startswith("sqlite"): + db_path = DATABASE_URL.replace("sqlite+aiosqlite:///", "").split("?")[0] + db_dir = os.path.dirname(db_path) + if db_dir and not os.path.exists(db_dir): + os.makedirs(db_dir, exist_ok=True) + logger.info(f"Created database directory: {db_dir}") # Create async engine with proper connection pool settings engine = create_async_engine( diff --git a/apps/coordinator-api/src/app/routers/client.py b/apps/coordinator-api/src/app/routers/client.py index da50a6f3..8e5f1b59 100755 --- a/apps/coordinator-api/src/app/routers/client.py +++ b/apps/coordinator-api/src/app/routers/client.py @@ -34,20 +34,28 @@ async def submit_job( service = JobService(session) job = service.create_job(client_id, req) - # Create payment if amount is specified + # Create payment if amount is specified (optional for proof-of-concept) if req.payment_amount and req.payment_amount > 0: - payment_service = PaymentService(session) - payment_create = JobPaymentCreate( - job_id=job.id, - amount=req.payment_amount, - currency=req.payment_currency, - payment_method="aitbc_token", # Jobs use AITBC tokens - ) - payment = await payment_service.create_payment(job.id, payment_create) - job.payment_id = payment.id - job.payment_status = payment.status - session.commit() - session.refresh(job) + try: + payment_service = PaymentService(session) + payment_create = JobPaymentCreate( + job_id=job.id, + amount=req.payment_amount, + currency=req.payment_currency, + payment_method="aitbc_token", # Jobs use AITBC tokens + ) + payment = await payment_service.create_payment(job.id, payment_create) + job.payment_id = payment.id + job.payment_status = payment.status + session.commit() + session.refresh(job) + logger.info(f"Payment created for job {job.id}: {payment.id}") + except Exception as e: + # Payment creation failure should not block job submission for proof-of-concept + logger.warning(f"Payment creation failed for job {job.id}, proceeding without payment: {e}") + job.payment_status = "skipped" + session.commit() + session.refresh(job) return service.to_view(job) # type: ignore[no-any-return]