refactor(api): split views_api.py into a package of domain modules
Pure move — no logic changes. The 4,100-line single file becomes views_api/ with one module per domain (accounts, entries, payments, settings_reports, reconciliation, permissions, admin), each registering full literal paths on its own APIRouter; __init__ builds the combined libra_api_router so libra/__init__.py is untouched. Shared imports/helpers live in views_api/_shared.py; _extract_entry_id and _SYSTEM_LINK_PREFIXES move to beancount_format.py (they are pure entry-dict parsing). Route behavior is pinned by tests/test_route_table.py: the 73-route set is unchanged, and the two order-sensitive families (the shadowed /accounts/hierarchy wart — deliberately preserved here, fixed in the next commit — and the admin sync literal/param pair) keep their relative order inside a single module each. Addresses CODE-REVIEW-2026-06 structure findings (views_api monolith). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9e06fa0b2a
commit
a7d7740a3a
12 changed files with 4233 additions and 4130 deletions
177
views_api/_shared.py
Normal file
177
views_api/_shared.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
"""Shared imports and helpers for the views_api package.
|
||||
|
||||
Everything module-level that the pre-split views_api.py defined above
|
||||
its first route lives here; endpoint modules pull it in with a
|
||||
wildcard import plus explicit imports for underscore-prefixed names
|
||||
(which `import *` does not export).
|
||||
"""
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from http import HTTPStatus
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from loguru import logger
|
||||
from lnbits.core.models import User, WalletTypeInfo
|
||||
from lnbits.decorators import (
|
||||
check_super_user,
|
||||
check_user_exists,
|
||||
require_invoice_key,
|
||||
)
|
||||
from lnbits.utils.exchange_rates import allowed_currencies, fiat_amount_as_satoshis
|
||||
|
||||
from ..account_utils import VALID_ACCOUNT_PREFIXES, validate_account_name
|
||||
from ..beancount_format import (
|
||||
_SYSTEM_LINK_PREFIXES,
|
||||
_extract_entry_id,
|
||||
fiat_rate_metadata,
|
||||
)
|
||||
from ..user_lookup import get_username
|
||||
from ..crud import (
|
||||
approve_manual_payment_request,
|
||||
check_balance_assertion,
|
||||
create_account,
|
||||
create_account_permission,
|
||||
create_balance_assertion,
|
||||
create_manual_payment_request,
|
||||
db,
|
||||
delete_account_permission,
|
||||
delete_balance_assertion,
|
||||
get_account,
|
||||
get_account_by_name,
|
||||
get_account_permission,
|
||||
get_account_permissions,
|
||||
get_all_accounts,
|
||||
get_all_manual_payment_requests,
|
||||
get_all_user_wallet_settings,
|
||||
get_balance_assertion,
|
||||
get_balance_assertions,
|
||||
get_manual_payment_request,
|
||||
get_or_create_user_account,
|
||||
get_user_manual_payment_requests,
|
||||
get_user_permissions,
|
||||
get_user_permissions_with_inheritance,
|
||||
reject_manual_payment_request,
|
||||
)
|
||||
from ..models import (
|
||||
Account,
|
||||
AccountPermission,
|
||||
AccountType,
|
||||
AccountWithPermissions,
|
||||
AssertionStatus,
|
||||
AssignUserRole,
|
||||
BalanceAssertion,
|
||||
BulkGrantPermission,
|
||||
BulkGrantResult,
|
||||
LibraSettings,
|
||||
CreateAccount,
|
||||
CreateAccountPermission,
|
||||
CreateChartAccount,
|
||||
CreateBalanceAssertion,
|
||||
CreateEntryLine,
|
||||
CreateJournalEntry,
|
||||
CreateManualPaymentRequest,
|
||||
CreateRole,
|
||||
CreateRolePermission,
|
||||
CreateUserEquityStatus,
|
||||
ExpenseEntry,
|
||||
GeneratePaymentInvoice,
|
||||
IncomeEntry,
|
||||
JournalEntry,
|
||||
JournalEntryFlag,
|
||||
ManualPaymentRequest,
|
||||
PayUser,
|
||||
PermissionType,
|
||||
ReceivableEntry,
|
||||
RecordPayment,
|
||||
RevenueEntry,
|
||||
Role,
|
||||
RolePermission,
|
||||
RoleWithPermissions,
|
||||
SettleReceivable,
|
||||
UpdateRole,
|
||||
UserBalance,
|
||||
UserEquityStatus,
|
||||
UserInfo,
|
||||
UserRole,
|
||||
UserWalletSettings,
|
||||
UserWithRoles,
|
||||
)
|
||||
from ..services import get_settings, get_user_wallet, update_settings, update_user_wallet
|
||||
from ..auth import (
|
||||
AuthContext,
|
||||
require_authenticated,
|
||||
require_authenticated_write,
|
||||
require_super_user,
|
||||
require_account_access,
|
||||
require_user_data_access,
|
||||
)
|
||||
|
||||
|
||||
# Synthetic Beancount flags marking auto-generated entries (summarization,
|
||||
# padding, transfers, conversions, unrealized gains, returns, merging) that
|
||||
# should not appear in user-facing transaction lists. Mirrors Fava's
|
||||
# _EXCL_FLAGS in fava/core/file.py.
|
||||
_SYNTHETIC_FLAGS = frozenset({"S", "T", "C", "P", "U", "R", "M"})
|
||||
|
||||
|
||||
# ===== HELPER FUNCTIONS =====
|
||||
|
||||
|
||||
async def check_libra_wallet_configured() -> str:
|
||||
"""Ensure libra wallet is configured, return wallet_id"""
|
||||
settings = await get_settings("admin")
|
||||
if not settings or not settings.libra_wallet_id:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="Libra wallet not configured. Please contact the super user to configure the Libra wallet in settings.",
|
||||
)
|
||||
return settings.libra_wallet_id
|
||||
|
||||
|
||||
async def check_user_wallet_configured(user_id: str) -> str:
|
||||
"""Ensure user has configured their wallet, return wallet_id"""
|
||||
from lnbits.settings import settings as lnbits_settings
|
||||
|
||||
# If user is super user, use the libra wallet
|
||||
if user_id == lnbits_settings.super_user:
|
||||
libra_settings = await get_settings("admin")
|
||||
if libra_settings and libra_settings.libra_wallet_id:
|
||||
return libra_settings.libra_wallet_id
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="Libra wallet not configured. Please configure the Libra wallet in settings.",
|
||||
)
|
||||
|
||||
# For regular users, check their personal wallet
|
||||
user_wallet = await get_user_wallet(user_id)
|
||||
if not user_wallet or not user_wallet.user_wallet_id:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="You must configure your wallet in settings before using this feature.",
|
||||
)
|
||||
return user_wallet.user_wallet_id
|
||||
|
||||
|
||||
# ===== UTILITY ENDPOINTS =====
|
||||
|
||||
|
||||
|
||||
_VALID_ACCOUNT_PREFIXES = VALID_ACCOUNT_PREFIXES
|
||||
|
||||
|
||||
def _validate_account_name(name: str) -> None:
|
||||
"""Raise HTTP 400 if ``name`` is not a syntactically valid Beancount account.
|
||||
|
||||
Thin HTTP wrapper around account_utils.validate_account_name — the
|
||||
single source of truth for account-name syntax (libra-#51).
|
||||
"""
|
||||
try:
|
||||
validate_account_name(name)
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail=str(e),
|
||||
)
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue