feat(v2): deposit CRUD + confirmation endpoints (P3b)
Adds 6 operator-scoped deposit endpoints:
POST /api/v1/dca/deposits — record fiat from an LP
(creator_user_id = the
operator who recorded)
GET /api/v1/dca/deposits — operator's deposits (all)
GET /api/v1/dca/deposits?client_id=X — scoped to one LP
GET /api/v1/dca/deposits/{id} — single
PUT /api/v1/dca/deposits/{id} — edit (pending only)
PUT /api/v1/dca/deposits/{id}/status — confirm/reject
DELETE /api/v1/dca/deposits/{id} — delete (pending only)
Cross-checks (client_id, machine_id) at create to prevent operators
binding deposits across machines incorrectly. Edits + deletes are
restricted to pending status so confirmed deposits become immutable
audit records (consistent with v1's existing behaviour from commit
28241e7).
Refs: aiolabs/satmachineadmin#9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7226b8289d
commit
b7f6f0a696
1 changed files with 116 additions and 0 deletions
116
views_api.py
116
views_api.py
|
|
@ -13,13 +13,18 @@ from lnbits.decorators import check_super_user, check_user_exists
|
||||||
|
|
||||||
from .crud import (
|
from .crud import (
|
||||||
create_dca_client,
|
create_dca_client,
|
||||||
|
create_deposit,
|
||||||
create_machine,
|
create_machine,
|
||||||
delete_dca_client,
|
delete_dca_client,
|
||||||
|
delete_deposit,
|
||||||
delete_machine,
|
delete_machine,
|
||||||
get_client_balance_summary,
|
get_client_balance_summary,
|
||||||
get_dca_client,
|
get_dca_client,
|
||||||
get_dca_clients_for_machine,
|
get_dca_clients_for_machine,
|
||||||
get_dca_clients_for_operator,
|
get_dca_clients_for_operator,
|
||||||
|
get_deposit,
|
||||||
|
get_deposits_for_client,
|
||||||
|
get_deposits_for_operator,
|
||||||
get_machine,
|
get_machine,
|
||||||
get_machines_for_operator,
|
get_machines_for_operator,
|
||||||
get_payments_for_operator,
|
get_payments_for_operator,
|
||||||
|
|
@ -28,19 +33,25 @@ from .crud import (
|
||||||
get_settlements_for_operator,
|
get_settlements_for_operator,
|
||||||
get_super_config,
|
get_super_config,
|
||||||
update_dca_client,
|
update_dca_client,
|
||||||
|
update_deposit,
|
||||||
|
update_deposit_status,
|
||||||
update_machine,
|
update_machine,
|
||||||
update_super_config,
|
update_super_config,
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
ClientBalanceSummary,
|
ClientBalanceSummary,
|
||||||
CreateDcaClientData,
|
CreateDcaClientData,
|
||||||
|
CreateDepositData,
|
||||||
CreateMachineData,
|
CreateMachineData,
|
||||||
DcaClient,
|
DcaClient,
|
||||||
|
DcaDeposit,
|
||||||
DcaPayment,
|
DcaPayment,
|
||||||
DcaSettlement,
|
DcaSettlement,
|
||||||
Machine,
|
Machine,
|
||||||
SuperConfig,
|
SuperConfig,
|
||||||
UpdateDcaClientData,
|
UpdateDcaClientData,
|
||||||
|
UpdateDepositData,
|
||||||
|
UpdateDepositStatusData,
|
||||||
UpdateMachineData,
|
UpdateMachineData,
|
||||||
UpdateSuperConfigData,
|
UpdateSuperConfigData,
|
||||||
)
|
)
|
||||||
|
|
@ -211,6 +222,111 @@ async def api_get_client_balance(
|
||||||
return summary
|
return summary
|
||||||
|
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Deposits — operator records fiat handed in by an LP at a machine.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
async def _deposit_owned_by(deposit_id: str, user_id: str) -> DcaDeposit:
|
||||||
|
deposit = await get_deposit(deposit_id)
|
||||||
|
if deposit is None:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "Deposit not found")
|
||||||
|
machine = await get_machine(deposit.machine_id)
|
||||||
|
if machine is None or machine.operator_user_id != user_id:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "Deposit not found")
|
||||||
|
return deposit
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineadmin_api_router.post(
|
||||||
|
"/api/v1/dca/deposits", response_model=DcaDeposit
|
||||||
|
)
|
||||||
|
async def api_create_deposit(
|
||||||
|
data: CreateDepositData, user: User = Depends(check_user_exists)
|
||||||
|
) -> DcaDeposit:
|
||||||
|
# Verify the (client_id, machine_id) pair belongs to the operator.
|
||||||
|
client = await _client_owned_by(data.client_id, user.id)
|
||||||
|
if client.machine_id != data.machine_id:
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
"client_id and machine_id refer to different machines",
|
||||||
|
)
|
||||||
|
return await create_deposit(user.id, data)
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineadmin_api_router.get(
|
||||||
|
"/api/v1/dca/deposits", response_model=list[DcaDeposit]
|
||||||
|
)
|
||||||
|
async def api_list_deposits(
|
||||||
|
client_id: str | None = None,
|
||||||
|
user: User = Depends(check_user_exists),
|
||||||
|
) -> list[DcaDeposit]:
|
||||||
|
"""Operator's deposits across all their machines; ?client_id scopes to
|
||||||
|
a single LP (with ownership check)."""
|
||||||
|
if client_id is not None:
|
||||||
|
await _client_owned_by(client_id, user.id)
|
||||||
|
return await get_deposits_for_client(client_id)
|
||||||
|
return await get_deposits_for_operator(user.id)
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineadmin_api_router.get(
|
||||||
|
"/api/v1/dca/deposits/{deposit_id}", response_model=DcaDeposit
|
||||||
|
)
|
||||||
|
async def api_get_deposit(
|
||||||
|
deposit_id: str, user: User = Depends(check_user_exists)
|
||||||
|
) -> DcaDeposit:
|
||||||
|
return await _deposit_owned_by(deposit_id, user.id)
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineadmin_api_router.put(
|
||||||
|
"/api/v1/dca/deposits/{deposit_id}", response_model=DcaDeposit
|
||||||
|
)
|
||||||
|
async def api_update_deposit(
|
||||||
|
deposit_id: str,
|
||||||
|
data: UpdateDepositData,
|
||||||
|
user: User = Depends(check_user_exists),
|
||||||
|
) -> DcaDeposit:
|
||||||
|
existing = await _deposit_owned_by(deposit_id, user.id)
|
||||||
|
if existing.status != "pending":
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
"Only pending deposits can be edited",
|
||||||
|
)
|
||||||
|
updated = await update_deposit(deposit_id, data)
|
||||||
|
if updated is None:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "Deposit not found")
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineadmin_api_router.put(
|
||||||
|
"/api/v1/dca/deposits/{deposit_id}/status", response_model=DcaDeposit
|
||||||
|
)
|
||||||
|
async def api_update_deposit_status(
|
||||||
|
deposit_id: str,
|
||||||
|
data: UpdateDepositStatusData,
|
||||||
|
user: User = Depends(check_user_exists),
|
||||||
|
) -> DcaDeposit:
|
||||||
|
await _deposit_owned_by(deposit_id, user.id)
|
||||||
|
updated = await update_deposit_status(deposit_id, data)
|
||||||
|
if updated is None:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "Deposit not found")
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
|
@satmachineadmin_api_router.delete(
|
||||||
|
"/api/v1/dca/deposits/{deposit_id}", status_code=HTTPStatus.NO_CONTENT
|
||||||
|
)
|
||||||
|
async def api_delete_deposit(
|
||||||
|
deposit_id: str, user: User = Depends(check_user_exists)
|
||||||
|
) -> None:
|
||||||
|
existing = await _deposit_owned_by(deposit_id, user.id)
|
||||||
|
if existing.status != "pending":
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
"Only pending deposits can be deleted",
|
||||||
|
)
|
||||||
|
await delete_deposit(deposit_id)
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Settlements (read-only at this phase; landing happens in tasks.py)
|
# Settlements (read-only at this phase; landing happens in tasks.py)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue