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>
177 lines
5.1 KiB
Python
177 lines
5.1 KiB
Python
"""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),
|
|
)
|
|
|
|
|