feat: add FastAPI REST backend
Expose all address generation functions as HTTP endpoints: - POST /api/hd/generate, /api/hd/encrypt, /api/hd/decrypt - POST /api/p2pk, /api/p2pkh, /api/p2sh, /api/p2wpkh, /api/p2tr - Add fastapi and uvicorn to requirements.txt
This commit is contained in:
@@ -5,3 +5,5 @@ six==1.17.0
|
||||
pytest
|
||||
bip-utils
|
||||
pycryptodome
|
||||
fastapi
|
||||
uvicorn
|
||||
|
||||
160
src/api.py
Normal file
160
src/api.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
FastAPI backend for the Bitcoin Address Generator.
|
||||
Exposes all address generation functions as HTTP endpoints.
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
try:
|
||||
from src.hd_wallet import generate_hd_wallet, encrypt_wallet, decrypt_wallet
|
||||
from src.p2pk import generate_p2pk
|
||||
from src.p2pkh import generate_legacy_address
|
||||
from src.p2sh import generate_p2sh_multisig
|
||||
from src.p2wpkh import generate_segwit_address
|
||||
from src.p2tr import generate_p2tr_address
|
||||
except ImportError:
|
||||
from hd_wallet import generate_hd_wallet, encrypt_wallet, decrypt_wallet
|
||||
from p2pk import generate_p2pk
|
||||
from p2pkh import generate_legacy_address
|
||||
from p2sh import generate_p2sh_multisig
|
||||
from p2wpkh import generate_segwit_address
|
||||
from p2tr import generate_p2tr_address
|
||||
|
||||
app = FastAPI(title="Bitcoin Address Generator API")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://localhost:5173", "http://localhost:4173", "file://", "*"],
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# ── HD Wallet ──────────────────────────────────────────────────────────────────
|
||||
|
||||
class HDGenerateRequest(BaseModel):
|
||||
network: str = "mainnet"
|
||||
bip_type: str = "bip84"
|
||||
mnemonic: Optional[str] = None
|
||||
passphrase: str = ""
|
||||
account: int = 0
|
||||
num_addresses: int = 5
|
||||
|
||||
|
||||
class HDEncryptRequest(BaseModel):
|
||||
wallet: Dict[str, Any]
|
||||
password: str
|
||||
|
||||
|
||||
class HDDecryptRequest(BaseModel):
|
||||
wallet: Dict[str, Any]
|
||||
password: str
|
||||
|
||||
|
||||
@app.post("/api/hd/generate")
|
||||
def hd_generate(req: HDGenerateRequest):
|
||||
try:
|
||||
return generate_hd_wallet(
|
||||
network=req.network,
|
||||
bip_type=req.bip_type,
|
||||
mnemonic=req.mnemonic or None,
|
||||
passphrase=req.passphrase,
|
||||
account=req.account,
|
||||
num_addresses=req.num_addresses,
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/hd/encrypt")
|
||||
def hd_encrypt(req: HDEncryptRequest):
|
||||
try:
|
||||
return encrypt_wallet(req.wallet, req.password)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/hd/decrypt")
|
||||
def hd_decrypt(req: HDDecryptRequest):
|
||||
try:
|
||||
return decrypt_wallet(req.wallet, req.password)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
# ── Single address types ───────────────────────────────────────────────────────
|
||||
|
||||
class P2PKRequest(BaseModel):
|
||||
network: str = "mainnet"
|
||||
compressed: bool = False
|
||||
|
||||
|
||||
class P2PKHRequest(BaseModel):
|
||||
network: str = "mainnet"
|
||||
compressed: bool = True
|
||||
|
||||
|
||||
class P2SHRequest(BaseModel):
|
||||
network: str = "mainnet"
|
||||
m: int = 2
|
||||
n: int = 3
|
||||
compressed: bool = True
|
||||
|
||||
|
||||
class P2WPKHRequest(BaseModel):
|
||||
network: str = "mainnet"
|
||||
compressed: bool = True
|
||||
|
||||
|
||||
class P2TRRequest(BaseModel):
|
||||
network: str = "mainnet"
|
||||
|
||||
|
||||
@app.post("/api/p2pk")
|
||||
def p2pk(req: P2PKRequest):
|
||||
try:
|
||||
return generate_p2pk(network=req.network, compressed=req.compressed)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/p2pkh")
|
||||
def p2pkh(req: P2PKHRequest):
|
||||
try:
|
||||
return generate_legacy_address(network=req.network, compressed=req.compressed)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/p2sh")
|
||||
def p2sh(req: P2SHRequest):
|
||||
try:
|
||||
return generate_p2sh_multisig(
|
||||
network=req.network, m=req.m, n=req.n, compressed=req.compressed
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/p2wpkh")
|
||||
def p2wpkh(req: P2WPKHRequest):
|
||||
try:
|
||||
return generate_segwit_address(network=req.network, compressed=req.compressed)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/p2tr")
|
||||
def p2tr(req: P2TRRequest):
|
||||
try:
|
||||
return generate_p2tr_address(network=req.network)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return {"status": "ok"}
|
||||
Reference in New Issue
Block a user