Update edge-api systemd service paths, switch to SQLite, add timezone-aware datetimes, and make job payments optional for proof-of-concept
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Waiting to run
Production Tests / Production Integration Tests (push) Waiting to run
Coverage Phase 1 (70% Target) / test-coverage-70 (push) Has been cancelled
Coverage Phase 2 (85% Target) / test-coverage-85 (push) Has been cancelled
Cross-Node Transaction Testing / transaction-test (push) Has been cancelled
Deploy to Testnet / deploy-testnet (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled

This commit is contained in:
aitbc
2026-05-27 12:38:19 +02:00
parent b58ca5db7c
commit 8f3e2dd7ac
4 changed files with 54 additions and 37 deletions

View File

@@ -4,17 +4,18 @@ After=network.target postgresql.service
[Service] [Service]
Type=simple Type=simple
WorkingDirectory=/opt/aitbc/apps/edge-api WorkingDirectory=/opt/aitbc/apps/aitbc-edge
Environment="PATH=/opt/aitbc/venv/bin" Environment="PATH=/opt/aitbc/venv/bin"
Environment="PYTHONPATH=/opt/aitbc/packages/py/aitbc-core/src:/opt/aitbc/apps/edge-api/src:/opt/aitbc" Environment="PYTHONPATH=/opt/aitbc/packages/py/aitbc-core/src:/opt/aitbc/apps/aitbc-edge/src:/opt/aitbc"
Environment="DATABASE_URL=postgresql+asyncpg://aitbc_edge:password@localhost:5432/aitbc_edge" Environment="DATABASE_URL=sqlite+aiosqlite:///opt/aitbc/data/edge.db"
Environment="DATA_DIR=/opt/aitbc/data"
Environment="BLOCKCHAIN_RPC_HOST=localhost" Environment="BLOCKCHAIN_RPC_HOST=localhost"
Environment="BLOCKCHAIN_RPC_PORT=8006" Environment="BLOCKCHAIN_RPC_PORT=8006"
Environment="GPU_SERVICE_HOST=localhost" Environment="GPU_SERVICE_HOST=localhost"
Environment="GPU_SERVICE_PORT=8101" Environment="GPU_SERVICE_PORT=8101"
Environment="JWT_SECRET_KEY=CQNLjrtnUVGzdO1skuLsxoiPEEmav2Vj3aA302cvo8I" Environment="JWT_SECRET_KEY=CQNLjrtnUVGzdO1skuLsxoiPEEmav2Vj3aA302cvo8I"
Environment="API_PORT=8103" 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 Restart=always
RestartSec=10 RestartSec=10

View File

@@ -1,10 +1,10 @@
"""Island-related schemas for Edge API Service""" """Island-related schemas for Edge API Service"""
from datetime import datetime from datetime import datetime, timezone
from enum import StrEnum from enum import StrEnum
from uuid import uuid4 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 from sqlmodel import Field, SQLModel
@@ -18,37 +18,37 @@ class IslandStatus(StrEnum):
class IslandMembership(SQLModel, table=True): class IslandMembership(SQLModel, table=True):
"""Island membership in edge API database""" """Island membership in edge API database"""
__tablename__ = "island_memberships" __tablename__ = "island_memberships"
__table_args__ = {"extend_existing": True} __table_args__ = {"extend_existing": True}
id: str = Field(default_factory=lambda: f"membership_{uuid4().hex[:8]}", primary_key=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_id: str = Field(sa_column=Column(String, index=True))
island_name: str island_name: str = Field(sa_column=Column(String))
chain_id: str = Field(sa_column=Column(index=True)) chain_id: str = Field(sa_column=Column(String, index=True))
status: IslandStatus = Field( status: IslandStatus = Field(
default=IslandStatus.ACTIVE, default=IslandStatus.ACTIVE,
sa_column=Column(SQLEnum(IslandStatus, values_only=True), index=True) sa_column=Column(SQLEnum(IslandStatus, values_only=True), index=True)
) )
role: str = Field(default="compute-provider") # compute-provider, consumer, hub role: str = Field(default="compute-provider", sa_column=Column(String)) # compute-provider, consumer, hub
joined_at: datetime = Field(default_factory=lambda: datetime.utcnow()) joined_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
peer_count: int = Field(default=0) peer_count: int = Field(default=0)
# Additional metadata # Additional metadata
extra_data: dict = Field(default_factory=dict, sa_column=Column(JSON, nullable=True)) extra_data: dict = Field(default_factory=dict, sa_column=Column(JSON, nullable=True))
class BridgeRequest(SQLModel, table=True): class BridgeRequest(SQLModel, table=True):
"""Bridge request for island connectivity""" """Bridge request for island connectivity"""
__tablename__ = "bridge_requests" __tablename__ = "bridge_requests"
__table_args__ = {"extend_existing": True} __table_args__ = {"extend_existing": True}
id: str = Field(default_factory=lambda: f"bridge_req_{uuid4().hex[:8]}", primary_key=True) id: str = Field(default_factory=lambda: f"bridge_req_{uuid4().hex[:8]}", primary_key=True)
request_id: str = Field(index=True) request_id: str = Field(sa_column=Column(String, index=True))
source_island_id: str source_island_id: str = Field(sa_column=Column(String))
target_island_id: str target_island_id: str = Field(sa_column=Column(String))
source_node_id: str source_node_id: str = Field(sa_column=Column(String))
status: str = Field(default="pending", index=True) # pending, approved, rejected status: str = Field(default="pending", sa_column=Column(String, index=True)) # pending, approved, rejected
created_at: datetime = Field(default_factory=lambda: datetime.utcnow()) created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.utcnow()) updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))

View File

@@ -12,7 +12,15 @@ from aitbc import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
# Database URL from environment variable or default # 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 # Create async engine with proper connection pool settings
engine = create_async_engine( engine = create_async_engine(

View File

@@ -34,20 +34,28 @@ async def submit_job(
service = JobService(session) service = JobService(session)
job = service.create_job(client_id, req) 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: if req.payment_amount and req.payment_amount > 0:
payment_service = PaymentService(session) try:
payment_create = JobPaymentCreate( payment_service = PaymentService(session)
job_id=job.id, payment_create = JobPaymentCreate(
amount=req.payment_amount, job_id=job.id,
currency=req.payment_currency, amount=req.payment_amount,
payment_method="aitbc_token", # Jobs use AITBC tokens 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 payment = await payment_service.create_payment(job.id, payment_create)
job.payment_status = payment.status job.payment_id = payment.id
session.commit() job.payment_status = payment.status
session.refresh(job) 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] return service.to_view(job) # type: ignore[no-any-return]