diff --git a/backend/.github/workflows/ci.yml b/backend/.github/workflows/ci.yml new file mode 100644 index 0000000..bcdd060 --- /dev/null +++ b/backend/.github/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + WP_ADMIN_KEY: ci-admin-key-for-testing + WP_VAULT_PASSWORD: ci-vault-password-for-testing + WP_DATA_DIR: /tmp/wp_ci_data + WP_RATE_LIMIT: "0" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install dependencies + run: | + pip install -r requirements.lock + pip install ruff mypy + - name: Ruff lint + run: ruff check . + - name: Ruff format check + run: ruff format --check . + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install dependencies + run: | + pip install -r requirements.lock + - name: Run tests + run: python3 -m pytest tests/ -v --tb=short --timeout=120 + + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install dependencies + run: | + pip install bandit gitleaks + - name: Bandit + run: bandit -r . -x tests + - name: Gitleaks + uses: gitleaks/gitleaks-action@v2 diff --git a/backend/.mypy_cache/3.12/cache.0.db b/backend/.mypy_cache/3.12/cache.0.db new file mode 100644 index 0000000..32452cf Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.0.db differ diff --git a/backend/.mypy_cache/3.12/cache.0.db-shm b/backend/.mypy_cache/3.12/cache.0.db-shm new file mode 100644 index 0000000..0c1c126 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.0.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.0.db-wal b/backend/.mypy_cache/3.12/cache.0.db-wal new file mode 100644 index 0000000..5fd42cb Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.0.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.1.db b/backend/.mypy_cache/3.12/cache.1.db new file mode 100644 index 0000000..88e0d9b Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.1.db differ diff --git a/backend/.mypy_cache/3.12/cache.1.db-shm b/backend/.mypy_cache/3.12/cache.1.db-shm new file mode 100644 index 0000000..d6085e7 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.1.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.1.db-wal b/backend/.mypy_cache/3.12/cache.1.db-wal new file mode 100644 index 0000000..e71fdbc Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.1.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.10.db b/backend/.mypy_cache/3.12/cache.10.db new file mode 100644 index 0000000..1aecfa1 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.10.db differ diff --git a/backend/.mypy_cache/3.12/cache.10.db-shm b/backend/.mypy_cache/3.12/cache.10.db-shm new file mode 100644 index 0000000..294a5e9 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.10.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.10.db-wal b/backend/.mypy_cache/3.12/cache.10.db-wal new file mode 100644 index 0000000..b907777 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.10.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.11.db b/backend/.mypy_cache/3.12/cache.11.db new file mode 100644 index 0000000..51a39f8 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.11.db differ diff --git a/backend/.mypy_cache/3.12/cache.11.db-shm b/backend/.mypy_cache/3.12/cache.11.db-shm new file mode 100644 index 0000000..a369ea9 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.11.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.11.db-wal b/backend/.mypy_cache/3.12/cache.11.db-wal new file mode 100644 index 0000000..eeb2f11 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.11.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.12.db b/backend/.mypy_cache/3.12/cache.12.db new file mode 100644 index 0000000..bf59fb6 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.12.db differ diff --git a/backend/.mypy_cache/3.12/cache.12.db-shm b/backend/.mypy_cache/3.12/cache.12.db-shm new file mode 100644 index 0000000..7ddde44 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.12.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.12.db-wal b/backend/.mypy_cache/3.12/cache.12.db-wal new file mode 100644 index 0000000..e5c7fda Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.12.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.13.db b/backend/.mypy_cache/3.12/cache.13.db new file mode 100644 index 0000000..5a7c88d Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.13.db differ diff --git a/backend/.mypy_cache/3.12/cache.13.db-shm b/backend/.mypy_cache/3.12/cache.13.db-shm new file mode 100644 index 0000000..f75854f Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.13.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.13.db-wal b/backend/.mypy_cache/3.12/cache.13.db-wal new file mode 100644 index 0000000..e9e2f32 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.13.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.14.db b/backend/.mypy_cache/3.12/cache.14.db new file mode 100644 index 0000000..813d48a Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.14.db differ diff --git a/backend/.mypy_cache/3.12/cache.14.db-shm b/backend/.mypy_cache/3.12/cache.14.db-shm new file mode 100644 index 0000000..df280bd Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.14.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.14.db-wal b/backend/.mypy_cache/3.12/cache.14.db-wal new file mode 100644 index 0000000..00bfd5d Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.14.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.15.db b/backend/.mypy_cache/3.12/cache.15.db new file mode 100644 index 0000000..52b9526 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.15.db differ diff --git a/backend/.mypy_cache/3.12/cache.15.db-shm b/backend/.mypy_cache/3.12/cache.15.db-shm new file mode 100644 index 0000000..38f981e Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.15.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.15.db-wal b/backend/.mypy_cache/3.12/cache.15.db-wal new file mode 100644 index 0000000..18fac97 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.15.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.2.db b/backend/.mypy_cache/3.12/cache.2.db new file mode 100644 index 0000000..e6ee8ba Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.2.db differ diff --git a/backend/.mypy_cache/3.12/cache.2.db-shm b/backend/.mypy_cache/3.12/cache.2.db-shm new file mode 100644 index 0000000..c9e3046 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.2.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.2.db-wal b/backend/.mypy_cache/3.12/cache.2.db-wal new file mode 100644 index 0000000..6d79e8b Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.2.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.3.db b/backend/.mypy_cache/3.12/cache.3.db new file mode 100644 index 0000000..68d9110 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.3.db differ diff --git a/backend/.mypy_cache/3.12/cache.3.db-shm b/backend/.mypy_cache/3.12/cache.3.db-shm new file mode 100644 index 0000000..614030b Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.3.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.3.db-wal b/backend/.mypy_cache/3.12/cache.3.db-wal new file mode 100644 index 0000000..0e853a7 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.3.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.4.db b/backend/.mypy_cache/3.12/cache.4.db new file mode 100644 index 0000000..cc1e192 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.4.db differ diff --git a/backend/.mypy_cache/3.12/cache.4.db-shm b/backend/.mypy_cache/3.12/cache.4.db-shm new file mode 100644 index 0000000..fd28577 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.4.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.4.db-wal b/backend/.mypy_cache/3.12/cache.4.db-wal new file mode 100644 index 0000000..8693f5f Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.4.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.5.db b/backend/.mypy_cache/3.12/cache.5.db new file mode 100644 index 0000000..844cf93 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.5.db differ diff --git a/backend/.mypy_cache/3.12/cache.5.db-shm b/backend/.mypy_cache/3.12/cache.5.db-shm new file mode 100644 index 0000000..d1de7c8 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.5.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.5.db-wal b/backend/.mypy_cache/3.12/cache.5.db-wal new file mode 100644 index 0000000..90f2b45 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.5.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.6.db b/backend/.mypy_cache/3.12/cache.6.db new file mode 100644 index 0000000..912d68c Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.6.db differ diff --git a/backend/.mypy_cache/3.12/cache.6.db-shm b/backend/.mypy_cache/3.12/cache.6.db-shm new file mode 100644 index 0000000..94460f6 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.6.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.6.db-wal b/backend/.mypy_cache/3.12/cache.6.db-wal new file mode 100644 index 0000000..2729671 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.6.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.7.db b/backend/.mypy_cache/3.12/cache.7.db new file mode 100644 index 0000000..4eb1275 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.7.db differ diff --git a/backend/.mypy_cache/3.12/cache.7.db-shm b/backend/.mypy_cache/3.12/cache.7.db-shm new file mode 100644 index 0000000..c8c42ef Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.7.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.7.db-wal b/backend/.mypy_cache/3.12/cache.7.db-wal new file mode 100644 index 0000000..a7e9b33 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.7.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.8.db b/backend/.mypy_cache/3.12/cache.8.db new file mode 100644 index 0000000..ab75d09 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.8.db differ diff --git a/backend/.mypy_cache/3.12/cache.8.db-shm b/backend/.mypy_cache/3.12/cache.8.db-shm new file mode 100644 index 0000000..fe40791 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.8.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.8.db-wal b/backend/.mypy_cache/3.12/cache.8.db-wal new file mode 100644 index 0000000..65d112c Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.8.db-wal differ diff --git a/backend/.mypy_cache/3.12/cache.9.db b/backend/.mypy_cache/3.12/cache.9.db new file mode 100644 index 0000000..f640784 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.9.db differ diff --git a/backend/.mypy_cache/3.12/cache.9.db-shm b/backend/.mypy_cache/3.12/cache.9.db-shm new file mode 100644 index 0000000..300499e Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.9.db-shm differ diff --git a/backend/.mypy_cache/3.12/cache.9.db-wal b/backend/.mypy_cache/3.12/cache.9.db-wal new file mode 100644 index 0000000..a33c0e8 Binary files /dev/null and b/backend/.mypy_cache/3.12/cache.9.db-wal differ diff --git a/backend/core/pdf.py b/backend/core/pdf.py index 2bb88f8..aeb17ee 100644 --- a/backend/core/pdf.py +++ b/backend/core/pdf.py @@ -7,6 +7,8 @@ import logging import time from typing import Optional +import qrcode + from core.config import cfg logger = logging.getLogger("wp.pdf") diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 6d48a8f..f470b00 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -41,3 +41,37 @@ build-backend = "setuptools.build_meta" [tool.setuptools.packages.find] where = ["."] include = ["walletpress*"] + +[tool.ruff] +target-version = "py310" +line-length = 100 +extend-exclude = [ + ".venv", + "venv", + "__pycache__", + ".mypy_cache", +] + +[tool.ruff.lint] +# We tolerate E402 (intentional imports for circular-dep avoidance in +# mcp_server, main.py, routers/airdrop.py, tests/) and E702 (one-line +# multi-statement idioms in plugins/defi.py for compact React-style code). +# F821 false-positives are tracked separately; the cli/verify_receipt.py +# one is intentional (parameter shadowing). +extend-ignore = [ + "E402", + "E702", +] + +[tool.ruff.lint.per-file-ignores] +# Tests may have imports after docstrings for setup convenience +"tests/*" = ["E402"] +# mcp_server uses local imports to break circular dependencies +"agent/mcp_server.py" = ["E402"] +"main.py" = ["E402"] +# routers/airdrop.py uses local imports for plugin isolation +"routers/airdrop.py" = ["E402"] +# plugins/__init__.py re-exports for plugin SDK public API +"plugins/__init__.py" = ["F401"] +# cli/verify_receipt.py — timestamp is a function parameter (F821 false positive) +"cli/verify_receipt.py" = ["F821"] diff --git a/backend/routers/chain_vault.py b/backend/routers/chain_vault.py index 5a14e54..832b5d7 100644 --- a/backend/routers/chain_vault.py +++ b/backend/routers/chain_vault.py @@ -9,13 +9,10 @@ ALWAYS, on ANY software. Verify with your own wallet software. from __future__ import annotations -import json import logging import secrets -import sqlite3 import time from datetime import UTC, datetime -from pathlib import Path from fastapi import APIRouter, HTTPException, Query, Request from pydantic import BaseModel, Field @@ -23,10 +20,9 @@ from pydantic import BaseModel, Field from core.auth import get_key_store from core.audit import get_audit from core.config import cfg -from core.proof import PROOF_VERSION, get_proof +from core.proof import get_proof from .tx_broadcaster import broadcast as broadcast_tx from core.vault import WalletEntry, get_vault -from ._persistent_store import PersistentStore as _PersistentStore from wallet_engine.chains import CHAINS, CHAIN_GROUPS, get_chains_for_tier from wallet_engine.generator import get_generator @@ -109,11 +105,6 @@ class DistributeRequest(BaseModel): distributions: list[dict] = Field(..., description="[{wallet_id, to_address, amount}]") -class ExportRequest(BaseModel): - format: str = Field(default="json", description="json, csv, env") - ids: list[str] = Field(default_factory=list, description="Specific wallet IDs (empty = all)") - - class ClusterRequest(BaseModel): chain: str = Field(...) count: int = Field(default=10, ge=1, le=1000) @@ -171,28 +162,10 @@ class TxBroadcastRequest(BaseModel): -class AlertDeleteRequest(BaseModel): - alert_id: str = Field(...) -class WebhookCreateRequest(BaseModel): - url: str = Field(...) - events: list[str] = Field(default=["wallet.generated"]) - secret: str = Field(default="") -class BulkFilterRequest(BaseModel): - filters: dict = Field(...) - - -class BulkDeleteRequest(BaseModel): - ids: list[str] = Field(...) - - -class BulkExportRequest(BaseModel): - format: str = Field(default="csv") - ids: list[str] = Field(default_factory=list) - class LinkProofRequest(BaseModel): source_address: str = Field(...) diff --git a/backend/routers/wallet_admin.py b/backend/routers/wallet_admin.py index cfb2045..7401d44 100644 --- a/backend/routers/wallet_admin.py +++ b/backend/routers/wallet_admin.py @@ -11,21 +11,18 @@ from __future__ import annotations import json import logging -import os import secrets import time -from datetime import UTC, datetime -from fastapi import APIRouter, HTTPException, Query, Request +from fastapi import APIRouter, HTTPException, Request from pydantic import BaseModel, Field from core.auth import get_key_store from core.audit import get_audit from core.config import cfg from core.proof import PROOF_VERSION, get_proof -from core.vault import WalletEntry, get_vault +from core.vault import get_vault from ._persistent_store import PersistentStore as _PersistentStore -from wallet_engine.generator import get_generator logger = logging.getLogger("wp.admin") @@ -43,6 +40,10 @@ class AlertCreateRequest(BaseModel): config: dict = Field(default_factory=dict) + +class AlertDeleteRequest(BaseModel): + alert_id: str = Field(...) + class WebhookCreateRequest(BaseModel): url: str = Field(...) events: list[str] = Field(default_factory=lambda: ["wallet.generated"]) @@ -62,6 +63,16 @@ class BulkDeleteRequest(BaseModel): wallet_ids: list[str] = Field(...) +class BulkExportRequest(BaseModel): + format: str = Field(default="csv") + ids: list[str] = Field(default_factory=list) + + +class ExportRequest(BaseModel): + format: str = Field(default="json", description="json, csv, env") + ids: list[str] = Field(default_factory=list, description="Specific wallet IDs (empty = all)") + + # Use a fresh router for admin endpoints — mounted under /api/v1/chain-vault # so the URL paths are unchanged for existing API consumers. router = APIRouter(prefix="/api/v1/chain-vault", tags=["Chain Vault Admin"])